{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "1965fde6",
   "metadata": {},
   "source": [
    "### In this notebook we perform Centralized learning, almost like 02.Individual_Training. \n",
    "\n",
    "In centralized learning there is an entity that has access to data from all base stations.\n",
    "Here, there is no option to filter out any base station.\n",
    "In this setting we also measure the energy consumption using the Carbontracker tool."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "id": "c11879af",
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-08-13T23:36:36.472502Z",
     "end_time": "2023-08-13T23:36:36.620473Z"
    }
   },
   "outputs": [],
   "source": [
    "import sys\n",
    "import os\n",
    "\n",
    "from pathlib import Path\n",
    "\n",
    "parent = Path(os.path.abspath(\"\")).resolve().parents[0]\n",
    "if parent not in sys.path:\n",
    "    sys.path.insert(0, str(parent))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 62,
   "id": "41f6cbc7",
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-08-13T23:36:36.476933Z",
     "end_time": "2023-08-13T23:36:36.983037Z"
    }
   },
   "outputs": [],
   "source": [
    "import random\n",
    "\n",
    "import numpy as np\n",
    "import torch\n",
    "import pandas as pd\n",
    "\n",
    "\n",
    "from matplotlib import pyplot as plt\n",
    "\n",
    "from argparse import Namespace"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 63,
   "id": "9d264d76",
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-08-13T23:36:36.494473Z",
     "end_time": "2023-08-13T23:36:36.999037Z"
    }
   },
   "outputs": [],
   "source": [
    "from ml.utils.data_utils import read_data, generate_time_lags, time_to_feature, handle_nans, to_Xy, \\\n",
    "    to_torch_dataset, to_timeseries_rep, assign_statistics, \\\n",
    "    to_train_val, scale_features, get_data_by_area, remove_identifiers, get_exogenous_data_by_area, handle_outliers"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 64,
   "id": "48559200",
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-08-13T23:36:36.508471Z",
     "end_time": "2023-08-13T23:36:37.002037Z"
    }
   },
   "outputs": [],
   "source": [
    "from ml.utils.train_utils import train, test"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 65,
   "id": "67c5e96b",
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-08-13T23:36:36.526471Z",
     "end_time": "2023-08-13T23:36:37.002037Z"
    }
   },
   "outputs": [],
   "source": [
    "from ml.models.mlp import MLP\n",
    "from ml.models.rnn import RNN\n",
    "from ml.models.lstm import LSTM\n",
    "from ml.models.gru import GRU\n",
    "from ml.models.cnn import CNN\n",
    "from ml.models.rnn_autoencoder import DualAttentionAutoEncoder\n",
    "from ml.utils.helpers import accumulate_metric"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 66,
   "id": "81e60d1f",
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-08-13T23:36:36.542492Z",
     "end_time": "2023-08-13T23:36:37.002037Z"
    }
   },
   "outputs": [],
   "source": [
    "args = Namespace(\n",
    "    data_path='../mydatase/marketData/full_dataset.csv',  # dataset\n",
    "    data_path_test=['../mydatase/marketData/Stort1_test.csv'],  # test dataset 这里实际并没有用上\n",
    "    test_size=0.2,  # validation size\n",
    "    targets=['Weekly_Sales'],  # the target columns\n",
    "    num_lags=5,  # the number of past observations to feed as input\n",
    "    filter_bs=None,  # whether to use a single bs for training. It will be changed dynamically\n",
    "    identifier='District',  # the column name that identifies a bs\n",
    "    nan_constant=0,  # the constant to transform nan values\n",
    "    x_scaler='minmax',  # x_scaler\n",
    "    y_scaler='minmax',  # y_scaler\n",
    "    outlier_detection=None,  # whether to perform flooring and capping\n",
    "    criterion='mse',  # optimization criterion, mse or l1\n",
    "    epochs=150,  # the number of maximum epochs\n",
    "    lr=0.001,  # learning rate\n",
    "    optimizer='adam',  # the optimizer, it can be sgd or adam\n",
    "    batch_size=128,  # the batch size to use\n",
    "    early_stopping=True,  # whether to use early stopping\n",
    "    patience=50,  # patience value for the early stopping parameter (if specified)\n",
    "    max_grad_norm=0.0,  # whether to clip grad norm\n",
    "    reg1=0.0,  # l1 regularization\n",
    "    reg2=0.0,  # l2 regularization\n",
    "    plot_history=True,  # plot loss history\n",
    "    cuda=True,  # whether to use gpu\n",
    "    seed=0,  # reproducibility\n",
    "    assign_stats=None,\n",
    "    # whether to use statistics as exogenous data, [\"mean\", \"median\", \"std\", \"variance\", \"kurtosis\", \"skew\"]\n",
    "    use_time_features=False  # whether to use datetime features\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "id": "0660fe4a",
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-08-13T23:36:36.556472Z",
     "end_time": "2023-08-13T23:36:37.003038Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Script arguments: Namespace(data_path='../mydatase/marketData/full_dataset.csv', data_path_test=['../mydatase/marketData/Stort1_test.csv'], test_size=0.2, targets=['Weekly_Sales'], num_lags=5, filter_bs=None, identifier='District', nan_constant=0, x_scaler='minmax', y_scaler='minmax', outlier_detection=None, criterion='mse', epochs=150, lr=0.001, optimizer='adam', batch_size=128, early_stopping=True, patience=50, max_grad_norm=0.0, reg1=0.0, reg2=0.0, plot_history=True, cuda=True, seed=0, assign_stats=None, use_time_features=False)\n",
      "\n"
     ]
    }
   ],
   "source": [
    "print(f\"Script arguments: {args}\\n\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "id": "1e574ee4",
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-08-13T23:36:36.573472Z",
     "end_time": "2023-08-13T23:36:37.003038Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Using cuda\n"
     ]
    }
   ],
   "source": [
    "device = \"cuda\" if args.cuda and torch.cuda.is_available() else \"cpu\"\n",
    "print(f\"Using {device}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 69,
   "id": "26b20d98",
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-08-13T23:36:36.589472Z",
     "end_time": "2023-08-13T23:36:37.003038Z"
    }
   },
   "outputs": [],
   "source": [
    "# Outlier detection specification\n",
    "# if args.outlier_detection is not None:\n",
    "#     outlier_columns = ['rb_down', 'rb_up', 'down', 'up']\n",
    "#     outlier_kwargs = {\"ElBorn\": (10, 90), \"LesCorts\": (10, 90), \"PobleSec\": (5, 95)}\n",
    "#     args.outlier_columns = outlier_columns\n",
    "#     args.outlier_kwargs = outlier_kwargs"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 70,
   "id": "49d661a6",
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-08-13T23:36:36.605472Z",
     "end_time": "2023-08-13T23:36:37.003038Z"
    }
   },
   "outputs": [],
   "source": [
    "def seed_all():\n",
    "    # ensure reproducibility\n",
    "    random.seed(args.seed)\n",
    "    np.random.seed(args.seed)\n",
    "    torch.manual_seed(args.seed)\n",
    "    torch.cuda.manual_seed_all(args.seed)\n",
    "    torch.backends.cudnn.deterministic = True\n",
    "    torch.backends.cudnn.benchmark = False"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 71,
   "id": "e3b0bafa",
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-08-13T23:36:36.620473Z",
     "end_time": "2023-08-13T23:36:37.003038Z"
    }
   },
   "outputs": [],
   "source": [
    "seed_all()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cebd59b3",
   "metadata": {},
   "source": [
    "### By setting filter_bs to None, the preprocessing pipeline returns data from all three base stations"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 72,
   "id": "acf0480d",
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-08-13T23:36:36.641473Z",
     "end_time": "2023-08-13T23:36:37.003038Z"
    }
   },
   "outputs": [],
   "source": [
    "def make_preprocessing(filter_bs=None):\n",
    "    \"\"\"Preprocess a given .csv\"\"\"\n",
    "    # read data\n",
    "    df = read_data(args.data_path, filter_data=filter_bs)\n",
    "    # handle nans\n",
    "    df = handle_nans(train_data=df, constant=args.nan_constant,\n",
    "                     identifier=args.identifier)\n",
    "    # split to train/validation\n",
    "    train_data, val_data = to_train_val(df)\n",
    "    \n",
    "    # handle outliers (if specified)\n",
    "    if args.outlier_detection is not None:\n",
    "        train_data = handle_outliers(df=train_data, columns=args.outlier_columns,\n",
    "                                     identifier=args.identifier, kwargs=args.outlier_kwargs)\n",
    "    \n",
    "    # get X and y\n",
    "    X_train, X_val, y_train, y_val = to_Xy(train_data=train_data, val_data=val_data,\n",
    "                                          targets=args.targets)\n",
    "    \n",
    "    # scale X\n",
    "    X_train, X_val, x_scaler = scale_features(train_data=X_train, val_data=X_val,\n",
    "                                             scaler=args.x_scaler, identifier=args.identifier)\n",
    "    # scale y\n",
    "    y_train, y_val, y_scaler = scale_features(train_data=y_train, val_data=y_val,\n",
    "                                             scaler=args.y_scaler, identifier=args.identifier)\n",
    "    \n",
    "    # generate time lags\n",
    "    X_train = generate_time_lags(X_train, args.num_lags)\n",
    "    X_val = generate_time_lags(X_val, args.num_lags)\n",
    "    y_train = generate_time_lags(y_train, args.num_lags, is_y=True)\n",
    "    y_val = generate_time_lags(y_val, args.num_lags, is_y=True)\n",
    "    \n",
    "    # get datetime features as exogenous data\n",
    "    date_time_df_train = time_to_feature(\n",
    "        X_train, args.use_time_features, identifier=args.identifier\n",
    "    )\n",
    "    date_time_df_val = time_to_feature(\n",
    "        X_val, args.use_time_features, identifier=args.identifier\n",
    "    )\n",
    "    \n",
    "    # get statistics as exogenous data\n",
    "    stats_df_train = assign_statistics(X_train, args.assign_stats, args.num_lags,\n",
    "                                       targets=args.targets, identifier=args.identifier)\n",
    "    stats_df_val = assign_statistics(X_val, args.assign_stats, args.num_lags, \n",
    "                                       targets=args.targets, identifier=args.identifier)\n",
    "    \n",
    "    # concat the exogenous features (if any) to a single dataframe\n",
    "    if date_time_df_train is not None or stats_df_train is not None:\n",
    "        exogenous_data_train = pd.concat([date_time_df_train, stats_df_train], axis=1)\n",
    "        # remove duplicate columns (if any)\n",
    "        exogenous_data_train = exogenous_data_train.loc[:, ~exogenous_data_train.columns.duplicated()].copy()\n",
    "        assert len(exogenous_data_train) == len(X_train) == len(y_train)\n",
    "    else:\n",
    "        exogenous_data_train = None\n",
    "    if date_time_df_val is not None or stats_df_val is not None:\n",
    "        exogenous_data_val = pd.concat([date_time_df_val, stats_df_val], axis=1)\n",
    "        exogenous_data_val = exogenous_data_val.loc[:, ~exogenous_data_val.columns.duplicated()].copy()\n",
    "        assert len(exogenous_data_val) == len(X_val) == len(y_val)\n",
    "    else:\n",
    "        exogenous_data_val = None\n",
    "        \n",
    "    return X_train, X_val, y_train, y_val, exogenous_data_train, exogenous_data_val, x_scaler, y_scaler"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 73,
   "id": "85eb3771",
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-08-13T23:36:36.653473Z",
     "end_time": "2023-08-13T23:36:38.129676Z"
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO logger 2023-08-13 23:36:36,669 | data_utils.py:382 | Observations info in Stort1\n",
      "INFO logger 2023-08-13 23:36:36,670 | data_utils.py:383 | \tTotal number of samples:  143\n",
      "INFO logger 2023-08-13 23:36:36,670 | data_utils.py:384 | \tNumber of samples for training: 115\n",
      "INFO logger 2023-08-13 23:36:36,671 | data_utils.py:385 | \tNumber of samples for validation:  28\n",
      "INFO logger 2023-08-13 23:36:36,672 | data_utils.py:382 | Observations info in Stort2\n",
      "INFO logger 2023-08-13 23:36:36,673 | data_utils.py:383 | \tTotal number of samples:  143\n",
      "INFO logger 2023-08-13 23:36:36,673 | data_utils.py:384 | \tNumber of samples for training: 115\n",
      "INFO logger 2023-08-13 23:36:36,674 | data_utils.py:385 | \tNumber of samples for validation:  28\n",
      "INFO logger 2023-08-13 23:36:36,675 | data_utils.py:382 | Observations info in Stort3\n",
      "INFO logger 2023-08-13 23:36:36,676 | data_utils.py:383 | \tTotal number of samples:  143\n",
      "INFO logger 2023-08-13 23:36:36,676 | data_utils.py:384 | \tNumber of samples for training: 115\n",
      "INFO logger 2023-08-13 23:36:36,676 | data_utils.py:385 | \tNumber of samples for validation:  28\n",
      "INFO logger 2023-08-13 23:36:36,677 | data_utils.py:382 | Observations info in Stort4\n",
      "INFO logger 2023-08-13 23:36:36,678 | data_utils.py:383 | \tTotal number of samples:  143\n",
      "INFO logger 2023-08-13 23:36:36,678 | data_utils.py:384 | \tNumber of samples for training: 115\n",
      "INFO logger 2023-08-13 23:36:36,678 | data_utils.py:385 | \tNumber of samples for validation:  28\n",
      "INFO logger 2023-08-13 23:36:36,679 | data_utils.py:382 | Observations info in Stort5\n",
      "INFO logger 2023-08-13 23:36:36,680 | data_utils.py:383 | \tTotal number of samples:  143\n",
      "INFO logger 2023-08-13 23:36:36,680 | data_utils.py:384 | \tNumber of samples for training: 115\n",
      "INFO logger 2023-08-13 23:36:36,681 | data_utils.py:385 | \tNumber of samples for validation:  28\n",
      "INFO logger 2023-08-13 23:36:36,682 | data_utils.py:382 | Observations info in Stort6\n",
      "INFO logger 2023-08-13 23:36:36,683 | data_utils.py:383 | \tTotal number of samples:  143\n",
      "INFO logger 2023-08-13 23:36:36,683 | data_utils.py:384 | \tNumber of samples for training: 115\n",
      "INFO logger 2023-08-13 23:36:36,684 | data_utils.py:385 | \tNumber of samples for validation:  28\n",
      "INFO logger 2023-08-13 23:36:36,686 | data_utils.py:382 | Observations info in Stort7\n",
      "INFO logger 2023-08-13 23:36:36,686 | data_utils.py:383 | \tTotal number of samples:  143\n",
      "INFO logger 2023-08-13 23:36:36,687 | data_utils.py:384 | \tNumber of samples for training: 115\n",
      "INFO logger 2023-08-13 23:36:36,687 | data_utils.py:385 | \tNumber of samples for validation:  28\n",
      "INFO logger 2023-08-13 23:36:36,689 | data_utils.py:382 | Observations info in Stort8\n",
      "INFO logger 2023-08-13 23:36:36,689 | data_utils.py:383 | \tTotal number of samples:  143\n",
      "INFO logger 2023-08-13 23:36:36,689 | data_utils.py:384 | \tNumber of samples for training: 115\n",
      "INFO logger 2023-08-13 23:36:36,690 | data_utils.py:385 | \tNumber of samples for validation:  28\n",
      "INFO logger 2023-08-13 23:36:36,691 | data_utils.py:382 | Observations info in Stort9\n",
      "INFO logger 2023-08-13 23:36:36,692 | data_utils.py:383 | \tTotal number of samples:  143\n",
      "INFO logger 2023-08-13 23:36:36,692 | data_utils.py:384 | \tNumber of samples for training: 115\n",
      "INFO logger 2023-08-13 23:36:36,692 | data_utils.py:385 | \tNumber of samples for validation:  28\n",
      "INFO logger 2023-08-13 23:36:36,694 | data_utils.py:382 | Observations info in Stort10\n",
      "INFO logger 2023-08-13 23:36:36,694 | data_utils.py:383 | \tTotal number of samples:  143\n",
      "INFO logger 2023-08-13 23:36:36,695 | data_utils.py:384 | \tNumber of samples for training: 115\n",
      "INFO logger 2023-08-13 23:36:36,695 | data_utils.py:385 | \tNumber of samples for validation:  28\n",
      "INFO logger 2023-08-13 23:36:36,696 | data_utils.py:382 | Observations info in Stort11\n",
      "INFO logger 2023-08-13 23:36:36,697 | data_utils.py:383 | \tTotal number of samples:  143\n",
      "INFO logger 2023-08-13 23:36:36,697 | data_utils.py:384 | \tNumber of samples for training: 115\n",
      "INFO logger 2023-08-13 23:36:36,698 | data_utils.py:385 | \tNumber of samples for validation:  28\n",
      "INFO logger 2023-08-13 23:36:36,699 | data_utils.py:382 | Observations info in Stort12\n",
      "INFO logger 2023-08-13 23:36:36,700 | data_utils.py:383 | \tTotal number of samples:  143\n",
      "INFO logger 2023-08-13 23:36:36,700 | data_utils.py:384 | \tNumber of samples for training: 115\n",
      "INFO logger 2023-08-13 23:36:36,701 | data_utils.py:385 | \tNumber of samples for validation:  28\n",
      "INFO logger 2023-08-13 23:36:36,702 | data_utils.py:382 | Observations info in Stort13\n",
      "INFO logger 2023-08-13 23:36:36,703 | data_utils.py:383 | \tTotal number of samples:  143\n",
      "INFO logger 2023-08-13 23:36:36,703 | data_utils.py:384 | \tNumber of samples for training: 115\n",
      "INFO logger 2023-08-13 23:36:36,704 | data_utils.py:385 | \tNumber of samples for validation:  28\n",
      "INFO logger 2023-08-13 23:36:36,705 | data_utils.py:382 | Observations info in Stort14\n",
      "INFO logger 2023-08-13 23:36:36,705 | data_utils.py:383 | \tTotal number of samples:  143\n",
      "INFO logger 2023-08-13 23:36:36,706 | data_utils.py:384 | \tNumber of samples for training: 115\n",
      "INFO logger 2023-08-13 23:36:36,706 | data_utils.py:385 | \tNumber of samples for validation:  28\n",
      "INFO logger 2023-08-13 23:36:36,707 | data_utils.py:382 | Observations info in Stort15\n",
      "INFO logger 2023-08-13 23:36:36,708 | data_utils.py:383 | \tTotal number of samples:  143\n",
      "INFO logger 2023-08-13 23:36:36,708 | data_utils.py:384 | \tNumber of samples for training: 115\n",
      "INFO logger 2023-08-13 23:36:36,709 | data_utils.py:385 | \tNumber of samples for validation:  28\n",
      "INFO logger 2023-08-13 23:36:36,710 | data_utils.py:382 | Observations info in Stort16\n",
      "INFO logger 2023-08-13 23:36:36,711 | data_utils.py:383 | \tTotal number of samples:  143\n",
      "INFO logger 2023-08-13 23:36:36,711 | data_utils.py:384 | \tNumber of samples for training: 115\n",
      "INFO logger 2023-08-13 23:36:36,711 | data_utils.py:385 | \tNumber of samples for validation:  28\n",
      "INFO logger 2023-08-13 23:36:36,712 | data_utils.py:382 | Observations info in Stort17\n",
      "INFO logger 2023-08-13 23:36:36,713 | data_utils.py:383 | \tTotal number of samples:  143\n",
      "INFO logger 2023-08-13 23:36:36,713 | data_utils.py:384 | \tNumber of samples for training: 115\n",
      "INFO logger 2023-08-13 23:36:36,714 | data_utils.py:385 | \tNumber of samples for validation:  28\n",
      "INFO logger 2023-08-13 23:36:36,715 | data_utils.py:382 | Observations info in Stort18\n",
      "INFO logger 2023-08-13 23:36:36,715 | data_utils.py:383 | \tTotal number of samples:  143\n",
      "INFO logger 2023-08-13 23:36:36,715 | data_utils.py:384 | \tNumber of samples for training: 115\n",
      "INFO logger 2023-08-13 23:36:36,716 | data_utils.py:385 | \tNumber of samples for validation:  28\n",
      "INFO logger 2023-08-13 23:36:36,716 | data_utils.py:382 | Observations info in Stort19\n",
      "INFO logger 2023-08-13 23:36:36,718 | data_utils.py:383 | \tTotal number of samples:  143\n",
      "INFO logger 2023-08-13 23:36:36,718 | data_utils.py:384 | \tNumber of samples for training: 115\n",
      "INFO logger 2023-08-13 23:36:36,719 | data_utils.py:385 | \tNumber of samples for validation:  28\n",
      "INFO logger 2023-08-13 23:36:36,720 | data_utils.py:382 | Observations info in Stort20\n",
      "INFO logger 2023-08-13 23:36:36,720 | data_utils.py:383 | \tTotal number of samples:  143\n",
      "INFO logger 2023-08-13 23:36:36,721 | data_utils.py:384 | \tNumber of samples for training: 115\n",
      "INFO logger 2023-08-13 23:36:36,721 | data_utils.py:385 | \tNumber of samples for validation:  28\n",
      "INFO logger 2023-08-13 23:36:36,722 | data_utils.py:382 | Observations info in Stort21\n",
      "INFO logger 2023-08-13 23:36:36,723 | data_utils.py:383 | \tTotal number of samples:  143\n",
      "INFO logger 2023-08-13 23:36:36,723 | data_utils.py:384 | \tNumber of samples for training: 115\n",
      "INFO logger 2023-08-13 23:36:36,724 | data_utils.py:385 | \tNumber of samples for validation:  28\n",
      "INFO logger 2023-08-13 23:36:36,725 | data_utils.py:382 | Observations info in Stort22\n",
      "INFO logger 2023-08-13 23:36:36,725 | data_utils.py:383 | \tTotal number of samples:  143\n",
      "INFO logger 2023-08-13 23:36:36,725 | data_utils.py:384 | \tNumber of samples for training: 115\n",
      "INFO logger 2023-08-13 23:36:36,726 | data_utils.py:385 | \tNumber of samples for validation:  28\n",
      "INFO logger 2023-08-13 23:36:36,727 | data_utils.py:382 | Observations info in Stort23\n",
      "INFO logger 2023-08-13 23:36:36,727 | data_utils.py:383 | \tTotal number of samples:  143\n",
      "INFO logger 2023-08-13 23:36:36,727 | data_utils.py:384 | \tNumber of samples for training: 115\n",
      "INFO logger 2023-08-13 23:36:36,728 | data_utils.py:385 | \tNumber of samples for validation:  28\n",
      "INFO logger 2023-08-13 23:36:36,729 | data_utils.py:382 | Observations info in Stort24\n",
      "INFO logger 2023-08-13 23:36:36,730 | data_utils.py:383 | \tTotal number of samples:  143\n",
      "INFO logger 2023-08-13 23:36:36,730 | data_utils.py:384 | \tNumber of samples for training: 115\n",
      "INFO logger 2023-08-13 23:36:36,731 | data_utils.py:385 | \tNumber of samples for validation:  28\n",
      "INFO logger 2023-08-13 23:36:36,733 | data_utils.py:382 | Observations info in Stort25\n",
      "INFO logger 2023-08-13 23:36:36,733 | data_utils.py:383 | \tTotal number of samples:  143\n",
      "INFO logger 2023-08-13 23:36:36,734 | data_utils.py:384 | \tNumber of samples for training: 115\n",
      "INFO logger 2023-08-13 23:36:36,734 | data_utils.py:385 | \tNumber of samples for validation:  28\n",
      "INFO logger 2023-08-13 23:36:36,735 | data_utils.py:382 | Observations info in Stort26\n",
      "INFO logger 2023-08-13 23:36:36,736 | data_utils.py:383 | \tTotal number of samples:  143\n",
      "INFO logger 2023-08-13 23:36:36,736 | data_utils.py:384 | \tNumber of samples for training: 115\n",
      "INFO logger 2023-08-13 23:36:36,737 | data_utils.py:385 | \tNumber of samples for validation:  28\n",
      "INFO logger 2023-08-13 23:36:36,738 | data_utils.py:382 | Observations info in Stort27\n",
      "INFO logger 2023-08-13 23:36:36,739 | data_utils.py:383 | \tTotal number of samples:  143\n",
      "INFO logger 2023-08-13 23:36:36,739 | data_utils.py:384 | \tNumber of samples for training: 115\n",
      "INFO logger 2023-08-13 23:36:36,739 | data_utils.py:385 | \tNumber of samples for validation:  28\n",
      "INFO logger 2023-08-13 23:36:36,741 | data_utils.py:382 | Observations info in Stort28\n",
      "INFO logger 2023-08-13 23:36:36,741 | data_utils.py:383 | \tTotal number of samples:  143\n",
      "INFO logger 2023-08-13 23:36:36,742 | data_utils.py:384 | \tNumber of samples for training: 115\n",
      "INFO logger 2023-08-13 23:36:36,742 | data_utils.py:385 | \tNumber of samples for validation:  28\n",
      "INFO logger 2023-08-13 23:36:36,743 | data_utils.py:382 | Observations info in Stort29\n",
      "INFO logger 2023-08-13 23:36:36,743 | data_utils.py:383 | \tTotal number of samples:  143\n",
      "INFO logger 2023-08-13 23:36:36,744 | data_utils.py:384 | \tNumber of samples for training: 115\n",
      "INFO logger 2023-08-13 23:36:36,744 | data_utils.py:385 | \tNumber of samples for validation:  28\n",
      "INFO logger 2023-08-13 23:36:36,745 | data_utils.py:382 | Observations info in Stort30\n",
      "INFO logger 2023-08-13 23:36:36,746 | data_utils.py:383 | \tTotal number of samples:  143\n",
      "INFO logger 2023-08-13 23:36:36,747 | data_utils.py:384 | \tNumber of samples for training: 115\n",
      "INFO logger 2023-08-13 23:36:36,747 | data_utils.py:385 | \tNumber of samples for validation:  28\n",
      "INFO logger 2023-08-13 23:36:36,748 | data_utils.py:382 | Observations info in Stort31\n",
      "INFO logger 2023-08-13 23:36:36,749 | data_utils.py:383 | \tTotal number of samples:  143\n",
      "INFO logger 2023-08-13 23:36:36,749 | data_utils.py:384 | \tNumber of samples for training: 115\n",
      "INFO logger 2023-08-13 23:36:36,749 | data_utils.py:385 | \tNumber of samples for validation:  28\n",
      "INFO logger 2023-08-13 23:36:36,751 | data_utils.py:382 | Observations info in Stort32\n",
      "INFO logger 2023-08-13 23:36:36,752 | data_utils.py:383 | \tTotal number of samples:  143\n",
      "INFO logger 2023-08-13 23:36:36,752 | data_utils.py:384 | \tNumber of samples for training: 115\n",
      "INFO logger 2023-08-13 23:36:36,752 | data_utils.py:385 | \tNumber of samples for validation:  28\n",
      "INFO logger 2023-08-13 23:36:36,754 | data_utils.py:382 | Observations info in Stort33\n",
      "INFO logger 2023-08-13 23:36:36,754 | data_utils.py:383 | \tTotal number of samples:  143\n",
      "INFO logger 2023-08-13 23:36:36,755 | data_utils.py:384 | \tNumber of samples for training: 115\n",
      "INFO logger 2023-08-13 23:36:36,755 | data_utils.py:385 | \tNumber of samples for validation:  28\n",
      "INFO logger 2023-08-13 23:36:36,756 | data_utils.py:382 | Observations info in Stort34\n",
      "INFO logger 2023-08-13 23:36:36,757 | data_utils.py:383 | \tTotal number of samples:  143\n",
      "INFO logger 2023-08-13 23:36:36,757 | data_utils.py:384 | \tNumber of samples for training: 115\n",
      "INFO logger 2023-08-13 23:36:36,757 | data_utils.py:385 | \tNumber of samples for validation:  28\n",
      "INFO logger 2023-08-13 23:36:36,758 | data_utils.py:382 | Observations info in Stort35\n",
      "INFO logger 2023-08-13 23:36:36,759 | data_utils.py:383 | \tTotal number of samples:  143\n",
      "INFO logger 2023-08-13 23:36:36,759 | data_utils.py:384 | \tNumber of samples for training: 115\n",
      "INFO logger 2023-08-13 23:36:36,759 | data_utils.py:385 | \tNumber of samples for validation:  28\n",
      "INFO logger 2023-08-13 23:36:36,760 | data_utils.py:382 | Observations info in Stort36\n",
      "INFO logger 2023-08-13 23:36:36,761 | data_utils.py:383 | \tTotal number of samples:  143\n",
      "INFO logger 2023-08-13 23:36:36,761 | data_utils.py:384 | \tNumber of samples for training: 115\n",
      "INFO logger 2023-08-13 23:36:36,762 | data_utils.py:385 | \tNumber of samples for validation:  28\n",
      "INFO logger 2023-08-13 23:36:36,763 | data_utils.py:382 | Observations info in Stort37\n",
      "INFO logger 2023-08-13 23:36:36,764 | data_utils.py:383 | \tTotal number of samples:  143\n",
      "INFO logger 2023-08-13 23:36:36,764 | data_utils.py:384 | \tNumber of samples for training: 115\n",
      "INFO logger 2023-08-13 23:36:36,764 | data_utils.py:385 | \tNumber of samples for validation:  28\n",
      "INFO logger 2023-08-13 23:36:36,765 | data_utils.py:382 | Observations info in Stort38\n",
      "INFO logger 2023-08-13 23:36:36,766 | data_utils.py:383 | \tTotal number of samples:  143\n",
      "INFO logger 2023-08-13 23:36:36,766 | data_utils.py:384 | \tNumber of samples for training: 115\n",
      "INFO logger 2023-08-13 23:36:36,767 | data_utils.py:385 | \tNumber of samples for validation:  28\n",
      "INFO logger 2023-08-13 23:36:36,768 | data_utils.py:382 | Observations info in Stort39\n",
      "INFO logger 2023-08-13 23:36:36,768 | data_utils.py:383 | \tTotal number of samples:  143\n",
      "INFO logger 2023-08-13 23:36:36,768 | data_utils.py:384 | \tNumber of samples for training: 115\n",
      "INFO logger 2023-08-13 23:36:36,769 | data_utils.py:385 | \tNumber of samples for validation:  28\n",
      "INFO logger 2023-08-13 23:36:36,770 | data_utils.py:382 | Observations info in Stort40\n",
      "INFO logger 2023-08-13 23:36:36,770 | data_utils.py:383 | \tTotal number of samples:  143\n",
      "INFO logger 2023-08-13 23:36:36,770 | data_utils.py:384 | \tNumber of samples for training: 115\n",
      "INFO logger 2023-08-13 23:36:36,771 | data_utils.py:385 | \tNumber of samples for validation:  28\n",
      "INFO logger 2023-08-13 23:36:36,772 | data_utils.py:382 | Observations info in Stort41\n",
      "INFO logger 2023-08-13 23:36:36,774 | data_utils.py:383 | \tTotal number of samples:  143\n",
      "INFO logger 2023-08-13 23:36:36,774 | data_utils.py:384 | \tNumber of samples for training: 115\n",
      "INFO logger 2023-08-13 23:36:36,774 | data_utils.py:385 | \tNumber of samples for validation:  28\n",
      "INFO logger 2023-08-13 23:36:36,775 | data_utils.py:382 | Observations info in Stort42\n",
      "INFO logger 2023-08-13 23:36:36,776 | data_utils.py:383 | \tTotal number of samples:  143\n",
      "INFO logger 2023-08-13 23:36:36,776 | data_utils.py:384 | \tNumber of samples for training: 115\n",
      "INFO logger 2023-08-13 23:36:36,777 | data_utils.py:385 | \tNumber of samples for validation:  28\n",
      "INFO logger 2023-08-13 23:36:36,778 | data_utils.py:382 | Observations info in Stort43\n",
      "INFO logger 2023-08-13 23:36:36,779 | data_utils.py:383 | \tTotal number of samples:  143\n",
      "INFO logger 2023-08-13 23:36:36,780 | data_utils.py:384 | \tNumber of samples for training: 115\n",
      "INFO logger 2023-08-13 23:36:36,780 | data_utils.py:385 | \tNumber of samples for validation:  28\n",
      "INFO logger 2023-08-13 23:36:36,781 | data_utils.py:382 | Observations info in Stort44\n",
      "INFO logger 2023-08-13 23:36:36,782 | data_utils.py:383 | \tTotal number of samples:  143\n",
      "INFO logger 2023-08-13 23:36:36,782 | data_utils.py:384 | \tNumber of samples for training: 115\n",
      "INFO logger 2023-08-13 23:36:36,783 | data_utils.py:385 | \tNumber of samples for validation:  28\n",
      "INFO logger 2023-08-13 23:36:36,784 | data_utils.py:382 | Observations info in Stort45\n",
      "INFO logger 2023-08-13 23:36:36,784 | data_utils.py:383 | \tTotal number of samples:  143\n",
      "INFO logger 2023-08-13 23:36:36,784 | data_utils.py:384 | \tNumber of samples for training: 115\n",
      "INFO logger 2023-08-13 23:36:36,785 | data_utils.py:385 | \tNumber of samples for validation:  28\n",
      "INFO logger 2023-08-13 23:36:36,789 | data_utils.py:388 | Observations info using all data\n",
      "INFO logger 2023-08-13 23:36:36,789 | data_utils.py:389 | \tTotal number of samples:  6435\n",
      "INFO logger 2023-08-13 23:36:36,789 | data_utils.py:390 | \tNumber of samples for training: 5175\n",
      "INFO logger 2023-08-13 23:36:36,790 | data_utils.py:391 | \tNumber of samples for validation:  1260\n"
     ]
    }
   ],
   "source": [
    "X_train, X_val, y_train, y_val, exogenous_data_train, exogenous_data_val, x_scaler, y_scaler = make_preprocessing()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 74,
   "id": "45e64887",
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-08-13T23:36:38.130677Z",
     "end_time": "2023-08-13T23:36:38.145797Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": "            Unemployment_lag-5  CPI_lag-5  Fuel_Price_lag-5  \\\nDate                                                          \n2010-03-12            0.390754   0.857248          0.054885   \n2010-03-19            0.390754   0.858718          0.041713   \n2010-03-26            0.390754   0.859192          0.023052   \n2010-04-02            0.390754   0.859499          0.048848   \n2010-04-09            0.390754   0.859807          0.083974   \n\n            Temperature_lag-5  Holiday_Flag_lag-5  Weekly_Sales_lag-5  \\\nDate                                                                    \n2010-03-12           0.434149                 0.0            0.397291   \n2010-03-19           0.396967                 1.0            0.396811   \n2010-03-26           0.410861                 0.0            0.388501   \n2010-04-02           0.476419                 0.0            0.332458   \n2010-04-09           0.475147                 0.0            0.372661   \n\n            Unemployment_lag-4  CPI_lag-4  Fuel_Price_lag-4  \\\nDate                                                          \n2010-03-12            0.390754   0.858718          0.041713   \n2010-03-19            0.390754   0.859192          0.023052   \n2010-03-26            0.390754   0.859499          0.048848   \n2010-04-02            0.390754   0.859807          0.083974   \n2010-04-09            0.390754   0.860114          0.107025   \n\n            Temperature_lag-4  ...  Temperature_lag-2  Holiday_Flag_lag-2  \\\nDate                           ...                                          \n2010-03-12           0.396967  ...           0.476419                 0.0   \n2010-03-19           0.410861  ...           0.475147                 0.0   \n2010-03-26           0.476419  ...           0.585616                 0.0   \n2010-04-02           0.475147  ...           0.554207                 0.0   \n2010-04-09           0.585616  ...           0.523581                 0.0   \n\n            Weekly_Sales_lag-2  Unemployment_lag-1  CPI_lag-1  \\\nDate                                                            \n2010-03-12            0.332458            0.390754   0.859807   \n2010-03-19            0.372661            0.390754   0.860114   \n2010-03-26            0.340720            0.390754   0.858451   \n2010-04-02            0.349857            0.390754   0.856459   \n2010-04-09            0.330990            0.361504   0.854467   \n\n            Fuel_Price_lag-1  Temperature_lag-1  Holiday_Flag_lag-1  \\\nDate                                                                  \n2010-03-12          0.083974           0.475147                 0.0   \n2010-03-19          0.107025           0.585616                 0.0   \n2010-03-26          0.136114           0.554207                 0.0   \n2010-04-02          0.142700           0.523581                 0.0   \n2010-04-09          0.135565           0.629452                 0.0   \n\n            Weekly_Sales_lag-1  District  \nDate                                      \n2010-03-12            0.372661    Stort1  \n2010-03-19            0.340720    Stort1  \n2010-03-26            0.349857    Stort1  \n2010-04-02            0.330990    Stort1  \n2010-04-09            0.383790    Stort1  \n\n[5 rows x 31 columns]",
      "text/html": "<div>\n<style scoped>\n    .dataframe tbody tr th:only-of-type {\n        vertical-align: middle;\n    }\n\n    .dataframe tbody tr th {\n        vertical-align: top;\n    }\n\n    .dataframe thead th {\n        text-align: right;\n    }\n</style>\n<table border=\"1\" class=\"dataframe\">\n  <thead>\n    <tr style=\"text-align: right;\">\n      <th></th>\n      <th>Unemployment_lag-5</th>\n      <th>CPI_lag-5</th>\n      <th>Fuel_Price_lag-5</th>\n      <th>Temperature_lag-5</th>\n      <th>Holiday_Flag_lag-5</th>\n      <th>Weekly_Sales_lag-5</th>\n      <th>Unemployment_lag-4</th>\n      <th>CPI_lag-4</th>\n      <th>Fuel_Price_lag-4</th>\n      <th>Temperature_lag-4</th>\n      <th>...</th>\n      <th>Temperature_lag-2</th>\n      <th>Holiday_Flag_lag-2</th>\n      <th>Weekly_Sales_lag-2</th>\n      <th>Unemployment_lag-1</th>\n      <th>CPI_lag-1</th>\n      <th>Fuel_Price_lag-1</th>\n      <th>Temperature_lag-1</th>\n      <th>Holiday_Flag_lag-1</th>\n      <th>Weekly_Sales_lag-1</th>\n      <th>District</th>\n    </tr>\n    <tr>\n      <th>Date</th>\n      <th></th>\n      <th></th>\n      <th></th>\n      <th></th>\n      <th></th>\n      <th></th>\n      <th></th>\n      <th></th>\n      <th></th>\n      <th></th>\n      <th></th>\n      <th></th>\n      <th></th>\n      <th></th>\n      <th></th>\n      <th></th>\n      <th></th>\n      <th></th>\n      <th></th>\n      <th></th>\n      <th></th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr>\n      <th>2010-03-12</th>\n      <td>0.390754</td>\n      <td>0.857248</td>\n      <td>0.054885</td>\n      <td>0.434149</td>\n      <td>0.0</td>\n      <td>0.397291</td>\n      <td>0.390754</td>\n      <td>0.858718</td>\n      <td>0.041713</td>\n      <td>0.396967</td>\n      <td>...</td>\n      <td>0.476419</td>\n      <td>0.0</td>\n      <td>0.332458</td>\n      <td>0.390754</td>\n      <td>0.859807</td>\n      <td>0.083974</td>\n      <td>0.475147</td>\n      <td>0.0</td>\n      <td>0.372661</td>\n      <td>Stort1</td>\n    </tr>\n    <tr>\n      <th>2010-03-19</th>\n      <td>0.390754</td>\n      <td>0.858718</td>\n      <td>0.041713</td>\n      <td>0.396967</td>\n      <td>1.0</td>\n      <td>0.396811</td>\n      <td>0.390754</td>\n      <td>0.859192</td>\n      <td>0.023052</td>\n      <td>0.410861</td>\n      <td>...</td>\n      <td>0.475147</td>\n      <td>0.0</td>\n      <td>0.372661</td>\n      <td>0.390754</td>\n      <td>0.860114</td>\n      <td>0.107025</td>\n      <td>0.585616</td>\n      <td>0.0</td>\n      <td>0.340720</td>\n      <td>Stort1</td>\n    </tr>\n    <tr>\n      <th>2010-03-26</th>\n      <td>0.390754</td>\n      <td>0.859192</td>\n      <td>0.023052</td>\n      <td>0.410861</td>\n      <td>0.0</td>\n      <td>0.388501</td>\n      <td>0.390754</td>\n      <td>0.859499</td>\n      <td>0.048848</td>\n      <td>0.476419</td>\n      <td>...</td>\n      <td>0.585616</td>\n      <td>0.0</td>\n      <td>0.340720</td>\n      <td>0.390754</td>\n      <td>0.858451</td>\n      <td>0.136114</td>\n      <td>0.554207</td>\n      <td>0.0</td>\n      <td>0.349857</td>\n      <td>Stort1</td>\n    </tr>\n    <tr>\n      <th>2010-04-02</th>\n      <td>0.390754</td>\n      <td>0.859499</td>\n      <td>0.048848</td>\n      <td>0.476419</td>\n      <td>0.0</td>\n      <td>0.332458</td>\n      <td>0.390754</td>\n      <td>0.859807</td>\n      <td>0.083974</td>\n      <td>0.475147</td>\n      <td>...</td>\n      <td>0.554207</td>\n      <td>0.0</td>\n      <td>0.349857</td>\n      <td>0.390754</td>\n      <td>0.856459</td>\n      <td>0.142700</td>\n      <td>0.523581</td>\n      <td>0.0</td>\n      <td>0.330990</td>\n      <td>Stort1</td>\n    </tr>\n    <tr>\n      <th>2010-04-09</th>\n      <td>0.390754</td>\n      <td>0.859807</td>\n      <td>0.083974</td>\n      <td>0.475147</td>\n      <td>0.0</td>\n      <td>0.372661</td>\n      <td>0.390754</td>\n      <td>0.860114</td>\n      <td>0.107025</td>\n      <td>0.585616</td>\n      <td>...</td>\n      <td>0.523581</td>\n      <td>0.0</td>\n      <td>0.330990</td>\n      <td>0.361504</td>\n      <td>0.854467</td>\n      <td>0.135565</td>\n      <td>0.629452</td>\n      <td>0.0</td>\n      <td>0.383790</td>\n      <td>Stort1</td>\n    </tr>\n  </tbody>\n</table>\n<p>5 rows × 31 columns</p>\n</div>"
     },
     "execution_count": 74,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "X_train.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 75,
   "id": "591b150f",
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-08-13T23:36:38.145797Z",
     "end_time": "2023-08-13T23:36:38.171796Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": "            Weekly_Sales District\nDate                             \n2010-03-12      0.340720   Stort1\n2010-03-19      0.349857   Stort1\n2010-03-26      0.330990   Stort1\n2010-04-02      0.383790   Stort1\n2010-04-09      0.370059   Stort1",
      "text/html": "<div>\n<style scoped>\n    .dataframe tbody tr th:only-of-type {\n        vertical-align: middle;\n    }\n\n    .dataframe tbody tr th {\n        vertical-align: top;\n    }\n\n    .dataframe thead th {\n        text-align: right;\n    }\n</style>\n<table border=\"1\" class=\"dataframe\">\n  <thead>\n    <tr style=\"text-align: right;\">\n      <th></th>\n      <th>Weekly_Sales</th>\n      <th>District</th>\n    </tr>\n    <tr>\n      <th>Date</th>\n      <th></th>\n      <th></th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr>\n      <th>2010-03-12</th>\n      <td>0.340720</td>\n      <td>Stort1</td>\n    </tr>\n    <tr>\n      <th>2010-03-19</th>\n      <td>0.349857</td>\n      <td>Stort1</td>\n    </tr>\n    <tr>\n      <th>2010-03-26</th>\n      <td>0.330990</td>\n      <td>Stort1</td>\n    </tr>\n    <tr>\n      <th>2010-04-02</th>\n      <td>0.383790</td>\n      <td>Stort1</td>\n    </tr>\n    <tr>\n      <th>2010-04-09</th>\n      <td>0.370059</td>\n      <td>Stort1</td>\n    </tr>\n  </tbody>\n</table>\n</div>"
     },
     "execution_count": 75,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "y_train.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 76,
   "id": "a0fa465a",
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-08-13T23:36:38.163797Z",
     "end_time": "2023-08-13T23:36:38.271796Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": "(MinMaxScaler(), MinMaxScaler())"
     },
     "execution_count": 76,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x_scaler, y_scaler"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 77,
   "id": "55102f6f",
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-08-13T23:36:38.178796Z",
     "end_time": "2023-08-13T23:36:38.287796Z"
    }
   },
   "outputs": [],
   "source": [
    "def make_postprocessing(X_train, X_val, y_train, y_val, exogenous_data_train, exogenous_data_val, x_scaler, y_scaler):\n",
    "    \"\"\"Make data ready to be fed into ml algorithms\"\"\"\n",
    "    # if there are more than one specified areas, get the data per area\n",
    "    if X_train[args.identifier].nunique() != 1:\n",
    "        area_X_train, area_X_val, area_y_train, area_y_val = get_data_by_area(X_train, X_val,\n",
    "                                                                              y_train, y_val, \n",
    "                                                                              identifier=args.identifier)\n",
    "    else:\n",
    "        area_X_train, area_X_val, area_y_train, area_y_val = None, None, None, None\n",
    "\n",
    "    # Get the exogenous data per area.\n",
    "    if exogenous_data_train is not None:\n",
    "        exogenous_data_train, exogenous_data_val = get_exogenous_data_by_area(exogenous_data_train,\n",
    "                                                                              exogenous_data_val)\n",
    "    # transform to np\n",
    "    if area_X_train is not None:\n",
    "        for area in area_X_train:\n",
    "            tmp_X_train, tmp_y_train, tmp_X_val, tmp_y_val = remove_identifiers(\n",
    "                area_X_train[area], area_y_train[area], area_X_val[area], area_y_val[area])\n",
    "            tmp_X_train, tmp_y_train = tmp_X_train.to_numpy(), tmp_y_train.to_numpy()\n",
    "            tmp_X_val, tmp_y_val = tmp_X_val.to_numpy(), tmp_y_val.to_numpy()\n",
    "            area_X_train[area] = tmp_X_train\n",
    "            area_X_val[area] = tmp_X_val\n",
    "            area_y_train[area] = tmp_y_train\n",
    "            area_y_val[area] = tmp_y_val\n",
    "    \n",
    "    if exogenous_data_train is not None:\n",
    "        for area in exogenous_data_train:\n",
    "            exogenous_data_train[area] = exogenous_data_train[area].to_numpy()\n",
    "            exogenous_data_val[area] = exogenous_data_val[area].to_numpy()\n",
    "    \n",
    "    # remove identifiers from features, targets\n",
    "    X_train, y_train, X_val, y_val = remove_identifiers(X_train, y_train, X_val, y_val)\n",
    "    assert len(X_train.columns) == len(X_val.columns)\n",
    "    \n",
    "    num_features = len(X_train.columns) // args.num_lags\n",
    "    \n",
    "    # to timeseries representation\n",
    "    X_train = to_timeseries_rep(X_train.to_numpy(), num_lags=args.num_lags,\n",
    "                                            num_features=num_features)\n",
    "    X_val = to_timeseries_rep(X_val.to_numpy(), num_lags=args.num_lags,\n",
    "                                          num_features=num_features)\n",
    "    \n",
    "    if area_X_train is not None:\n",
    "        area_X_train = to_timeseries_rep(area_X_train, num_lags=args.num_lags,\n",
    "                                                     num_features=num_features)\n",
    "        area_X_val = to_timeseries_rep(area_X_val, num_lags=args.num_lags,\n",
    "                                                   num_features=num_features)\n",
    "    \n",
    "    # transform targets to numpy\n",
    "    y_train, y_val = y_train.to_numpy(), y_val.to_numpy()\n",
    "    \n",
    "    # centralized (all) learning specific\n",
    "    if not args.filter_bs and exogenous_data_train is not None:\n",
    "        exogenous_data_train_combined, exogenous_data_val_combined = [], []\n",
    "        for area in exogenous_data_train:\n",
    "            exogenous_data_train_combined.extend(exogenous_data_train[area])\n",
    "            exogenous_data_val_combined.extend(exogenous_data_val[area])\n",
    "        exogenous_data_train_combined = np.stack(exogenous_data_train_combined)\n",
    "        exogenous_data_val_combined = np.stack(exogenous_data_val_combined)\n",
    "        exogenous_data_train[\"all\"] = exogenous_data_train_combined\n",
    "        exogenous_data_val[\"all\"] = exogenous_data_val_combined\n",
    "    return X_train, X_val, y_train, y_val, area_X_train, area_X_val, area_y_train, area_y_val, exogenous_data_train, exogenous_data_val"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 77,
   "id": "698aa32c",
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-08-13T23:36:38.195798Z",
     "end_time": "2023-08-13T23:36:38.370799Z"
    }
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 78,
   "id": "ce3616bf",
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-08-13T23:36:38.214800Z",
     "end_time": "2023-08-13T23:36:38.383798Z"
    }
   },
   "outputs": [],
   "source": [
    "X_train, X_val, y_train, y_val, area_X_train, area_X_val, area_y_train, area_y_val, exogenous_data_train, exogenous_data_val = make_postprocessing(X_train, X_val, y_train, y_val, exogenous_data_train, exogenous_data_val, x_scaler, y_scaler)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 79,
   "id": "6592db61",
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-08-13T23:36:38.322797Z",
     "end_time": "2023-08-13T23:36:38.434799Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": "dict_keys(['Stort1', 'Stort2', 'Stort3', 'Stort4', 'Stort5', 'Stort6', 'Stort7', 'Stort8', 'Stort9', 'Stort10', 'Stort11', 'Stort12', 'Stort13', 'Stort14', 'Stort15', 'Stort16', 'Stort17', 'Stort18', 'Stort19', 'Stort20', 'Stort21', 'Stort22', 'Stort23', 'Stort24', 'Stort25', 'Stort26', 'Stort27', 'Stort28', 'Stort29', 'Stort30', 'Stort31', 'Stort32', 'Stort33', 'Stort34', 'Stort35', 'Stort36', 'Stort37', 'Stort38', 'Stort39', 'Stort40', 'Stort41', 'Stort42', 'Stort43', 'Stort44', 'Stort45'])"
     },
     "execution_count": 79,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "area_X_train.keys()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 80,
   "id": "d521ab70",
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-08-13T23:36:38.337798Z",
     "end_time": "2023-08-13T23:36:38.479798Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": "dict_keys(['Stort1', 'Stort2', 'Stort3', 'Stort4', 'Stort5', 'Stort6', 'Stort7', 'Stort8', 'Stort9', 'Stort10', 'Stort11', 'Stort12', 'Stort13', 'Stort14', 'Stort15', 'Stort16', 'Stort17', 'Stort18', 'Stort19', 'Stort20', 'Stort21', 'Stort22', 'Stort23', 'Stort24', 'Stort25', 'Stort26', 'Stort27', 'Stort28', 'Stort29', 'Stort30', 'Stort31', 'Stort32', 'Stort33', 'Stort34', 'Stort35', 'Stort36', 'Stort37', 'Stort38', 'Stort39', 'Stort40', 'Stort41', 'Stort42', 'Stort43', 'Stort44', 'Stort45'])"
     },
     "execution_count": 80,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "area_X_val.keys()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 81,
   "id": "c90c8a6d",
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-08-13T23:36:38.354798Z",
     "end_time": "2023-08-13T23:36:38.479798Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": "array([[[[0.39075384],\n         [0.85724819],\n         [0.05488479],\n         [0.43414876],\n         [0.        ],\n         [0.39729115]],\n\n        [[0.39075384],\n         [0.85871804],\n         [0.04171252],\n         [0.39696673],\n         [1.        ],\n         [0.3968108 ]],\n\n        [[0.39075384],\n         [0.85919154],\n         [0.02305162],\n         [0.41086107],\n         [0.        ],\n         [0.38850051]],\n\n        [[0.39075384],\n         [0.8594991 ],\n         [0.04884756],\n         [0.47641879],\n         [0.        ],\n         [0.33245805]],\n\n        [[0.39075384],\n         [0.85980666],\n         [0.08397377],\n         [0.47514677],\n         [0.        ],\n         [0.37266061]]],\n\n\n       [[[0.39075384],\n         [0.85871804],\n         [0.04171252],\n         [0.39696673],\n         [1.        ],\n         [0.3968108 ]],\n\n        [[0.39075384],\n         [0.85919154],\n         [0.02305162],\n         [0.41086107],\n         [0.        ],\n         [0.38850051]],\n\n        [[0.39075384],\n         [0.8594991 ],\n         [0.04884756],\n         [0.47641879],\n         [0.        ],\n         [0.33245805]],\n\n        [[0.39075384],\n         [0.85980666],\n         [0.08397377],\n         [0.47514677],\n         [0.        ],\n         [0.37266061]],\n\n        [[0.39075384],\n         [0.86011422],\n         [0.10702538],\n         [0.58561647],\n         [0.        ],\n         [0.34071973]]]])"
     },
     "execution_count": 81,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "X_train[:2]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 82,
   "id": "8a11d39b",
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-08-13T23:36:38.370799Z",
     "end_time": "2023-08-13T23:36:38.481798Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": "array([[0.34071973],\n       [0.34985712]], dtype=float32)"
     },
     "execution_count": 82,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "y_train[:2]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 83,
   "id": "076cc206",
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-08-13T23:36:38.387799Z",
     "end_time": "2023-08-13T23:36:38.524798Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": "(4950, 1035)"
     },
     "execution_count": 83,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len(X_train), len(X_val)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 83,
   "id": "d7777e72",
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-08-13T23:36:38.402798Z",
     "end_time": "2023-08-13T23:36:38.544797Z"
    }
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 84,
   "id": "a0274244",
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-08-13T23:36:38.417798Z",
     "end_time": "2023-08-13T23:36:38.544797Z"
    }
   },
   "outputs": [],
   "source": [
    "def get_input_dims(X_train, exogenous_data_train):\n",
    "    if args.model_name == \"mlp\":\n",
    "        input_dim = X_train.shape[1] * X_train.shape[2]\n",
    "    else:\n",
    "        input_dim = X_train.shape[2]\n",
    "    \n",
    "    if exogenous_data_train is not None:\n",
    "        if len(exogenous_data_train) == 1:\n",
    "            cid = next(iter(exogenous_data_train.keys()))\n",
    "            exogenous_dim = exogenous_data_train[cid].shape[1]\n",
    "        else:\n",
    "            exogenous_dim = exogenous_data_train[\"all\"].shape[1]\n",
    "    else:\n",
    "        exogenous_dim = 0\n",
    "    \n",
    "    return input_dim, exogenous_dim"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 85,
   "id": "e90c8fc3",
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-08-13T23:36:38.436798Z",
     "end_time": "2023-08-13T23:36:38.568798Z"
    }
   },
   "outputs": [],
   "source": [
    "def get_model(model: str,\n",
    "              input_dim: int,\n",
    "              out_dim: int,\n",
    "              lags: int = 5,\n",
    "              exogenous_dim: int = 0,\n",
    "              seed=0):\n",
    "    if model == \"mlp\":\n",
    "        model = MLP(input_dim=input_dim, layer_units=[256, 128, 64], num_outputs=out_dim)\n",
    "    elif model == \"rnn\":\n",
    "        model = RNN(input_dim=input_dim, rnn_hidden_size=128, num_rnn_layers=1, rnn_dropout=0.0,\n",
    "                    layer_units=[128], num_outputs=out_dim, matrix_rep=True, exogenous_dim=exogenous_dim)\n",
    "    elif model == \"lstm\":\n",
    "        model = LSTM(input_dim=input_dim, lstm_hidden_size=128, num_lstm_layers=1, lstm_dropout=0.0,\n",
    "                     layer_units=[128], num_outputs=out_dim, matrix_rep=True, exogenous_dim=exogenous_dim)\n",
    "    elif model == \"gru\":\n",
    "        model = GRU(input_dim=input_dim, gru_hidden_size=128, num_gru_layers=1, gru_dropout=0.0,\n",
    "                    layer_units=[128], num_outputs=out_dim, matrix_rep=True, exogenous_dim=exogenous_dim)\n",
    "    elif model == \"cnn\":\n",
    "        model = CNN(num_features=input_dim, lags=lags, exogenous_dim=exogenous_dim, out_dim=out_dim)\n",
    "    elif model == \"da_encoder_decoder\":\n",
    "        model = DualAttentionAutoEncoder(input_dim=input_dim, architecture=\"lstm\", matrix_rep=True)\n",
    "    else:\n",
    "        raise NotImplementedError(\"Specified model is not implemented. Plese define your own model or choose one from ['mlp', 'rnn', 'lstm', 'gru', 'cnn', 'da_encoder_decoder']\")\n",
    "    return model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 86,
   "id": "495af4a3",
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-08-13T23:36:38.450798Z",
     "end_time": "2023-08-13T23:36:38.568798Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "6 0\n"
     ]
    }
   ],
   "source": [
    "# define the model\n",
    "args.model_name = \"rnn\"\n",
    "\n",
    "input_dim, exogenous_dim = get_input_dims(X_train, exogenous_data_train)\n",
    "\n",
    "print(input_dim, exogenous_dim)\n",
    "\n",
    "model = get_model(model=args.model_name,\n",
    "                  input_dim=input_dim,\n",
    "                  out_dim=y_train.shape[1],\n",
    "                  lags=args.num_lags,\n",
    "                  exogenous_dim=exogenous_dim,\n",
    "                  seed=args.seed)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 87,
   "id": "b1933251",
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-08-13T23:36:38.465798Z",
     "end_time": "2023-08-13T23:36:38.568798Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": "RNN(\n  (rnn): RNN(6, 128, batch_first=True)\n  (MLP_layers): Sequential(\n    (0): Linear(in_features=128, out_features=1, bias=True)\n  )\n)"
     },
     "execution_count": 87,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 88,
   "id": "fa4b5479",
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-08-13T23:36:38.480799Z",
     "end_time": "2023-08-13T23:36:38.569800Z"
    }
   },
   "outputs": [],
   "source": [
    "def fit(model, X_train, y_train, X_val, y_val, \n",
    "        exogenous_data_train=None, exogenous_data_val=None, \n",
    "        idxs=[1], # the indices of our targets in X\n",
    "        log_per=1):\n",
    "    \n",
    "    # get exogenous data (if any)\n",
    "    if exogenous_data_train is not None and len(exogenous_data_train) > 1:\n",
    "        exogenous_data_train = exogenous_data_train[\"all\"]\n",
    "        exogenous_data_val = exogenous_data_val[\"all\"]\n",
    "    elif exogenous_data_train is not None and len(exogenous_data_train) == 1:\n",
    "        cid = next(iter(exogenous_data_train.keys()))\n",
    "        exogenous_data_train = exogenous_data_train[cid]\n",
    "        exogenous_data_val = exogenous_data_val[cid]\n",
    "    else:\n",
    "        exogenous_data_train = None\n",
    "        exogenous_data_val = None\n",
    "    num_features = len(X_train[0][0])\n",
    "    \n",
    "    # to torch loader\n",
    "    train_loader = to_torch_dataset(X_train, y_train,\n",
    "                                    num_lags=args.num_lags,\n",
    "                                    num_features=num_features,\n",
    "                                    exogenous_data=exogenous_data_train,\n",
    "                                    indices=idxs,\n",
    "                                    batch_size=args.batch_size, \n",
    "                                    shuffle=False)\n",
    "    val_loader = to_torch_dataset(X_val, y_val, \n",
    "                                  num_lags=args.num_lags,\n",
    "                                  num_features=num_features,\n",
    "                                  exogenous_data=exogenous_data_val,\n",
    "                                  indices=idxs,\n",
    "                                  batch_size=args.batch_size,\n",
    "                                  shuffle=False)\n",
    "    \n",
    "    # train the model\n",
    "    model = train(model, \n",
    "                  train_loader, val_loader,\n",
    "                  epochs=args.epochs,\n",
    "                  optimizer=args.optimizer, lr=args.lr,\n",
    "                  criterion=args.criterion,\n",
    "                  early_stopping=args.early_stopping,\n",
    "                  patience=args.patience,\n",
    "                  plot_history=args.plot_history, \n",
    "                  device=device, log_per=log_per,\n",
    "                  use_carbontracker=True)\n",
    "    \n",
    "    return model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 89,
   "id": "7edadc97",
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-08-13T23:36:38.495800Z",
     "end_time": "2023-08-13T23:36:50.927677Z"
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO logger 2023-08-13 23:36:38,814 | train_utils.py:97 | Epoch 1 [Train]: loss 0.043562634846565716, mse: 0.01387183926999569, rmse: 0.11777877257806557, mae 0.08705448359251022, r2: 0.4476615709398063, nrmse: 0.5065892907267834\n",
      "INFO logger 2023-08-13 23:36:38,815 | train_utils.py:99 | Epoch 1 [Test]: loss 9.739798019023765e-05, mse: 0.012436645105481148, rmse: 0.11151970725159364, mae 0.08581637591123581, r2: 0.4326582879971861, nrmse: 0.48403246341131434\n",
      "INFO logger 2023-08-13 23:36:38,816 | helpers.py:147 | Validation loss decreased (inf --> 0.000097). Caching model ...\n",
      "INFO logger 2023-08-13 23:36:39,074 | train_utils.py:97 | Epoch 2 [Train]: loss 0.008822131807205435, mse: 0.003486047498881817, rmse: 0.059042759919246804, mae 0.04036794230341911, r2: 0.8611951904793402, nrmse: 0.25395433502431775\n",
      "INFO logger 2023-08-13 23:36:39,075 | train_utils.py:99 | Epoch 2 [Test]: loss 1.3151069639637562e-05, mse: 0.0016439106548205018, rmse: 0.040545168082282035, mae 0.03125031664967537, r2: 0.9250071789336252, nrmse: 0.17597945753226787\n",
      "INFO logger 2023-08-13 23:36:39,075 | helpers.py:147 | Validation loss decreased (0.000097 --> 0.000013). Caching model ...\n",
      "INFO logger 2023-08-13 23:36:39,315 | train_utils.py:97 | Epoch 3 [Train]: loss 0.0031686010777962226, mse: 0.002391644986346364, rmse: 0.04890444751089991, mae 0.02995768003165722, r2: 0.9047712950599925, nrmse: 0.21034749162045388\n",
      "INFO logger 2023-08-13 23:36:39,316 | train_utils.py:99 | Epoch 3 [Test]: loss 4.971095030128524e-06, mse: 0.0006006519543007016, rmse: 0.02450820177615448, mae 0.019520018249750137, r2: 0.9725991306248496, nrmse: 0.10637371251998226\n",
      "INFO logger 2023-08-13 23:36:39,316 | helpers.py:147 | Validation loss decreased (0.000013 --> 0.000005). Caching model ...\n",
      "INFO logger 2023-08-13 23:36:39,515 | train_utils.py:97 | Epoch 4 [Train]: loss 0.0023698466352354256, mse: 0.0023011015728116035, rmse: 0.04796979854879113, mae 0.028841016814112663, r2: 0.9083764699981719, nrmse: 0.20632738558242078\n",
      "INFO logger 2023-08-13 23:36:39,515 | train_utils.py:99 | Epoch 4 [Test]: loss 4.007297164225132e-06, mse: 0.00048621449968777597, rmse: 0.022050272100084752, mae 0.017397837713360786, r2: 0.977819597893523, nrmse: 0.09570548369011503\n",
      "INFO logger 2023-08-13 23:36:39,516 | helpers.py:147 | Validation loss decreased (0.000005 --> 0.000004). Caching model ...\n",
      "INFO logger 2023-08-13 23:36:39,713 | train_utils.py:97 | Epoch 5 [Train]: loss 0.002314872971813505, mse: 0.002280946122482419, rmse: 0.04775925169516812, mae 0.02864096686244011, r2: 0.9091790135669267, nrmse: 0.20542178282475118\n",
      "INFO logger 2023-08-13 23:36:39,714 | train_utils.py:99 | Epoch 5 [Test]: loss 3.968413743743862e-06, mse: 0.00048122418229468167, rmse: 0.021936822520471865, mae 0.017081890255212784, r2: 0.9780472503060881, nrmse: 0.09521307494150599\n",
      "INFO logger 2023-08-13 23:36:39,714 | helpers.py:147 | Validation loss decreased (0.000004 --> 0.000004). Caching model ...\n",
      "INFO logger 2023-08-13 23:36:39,910 | train_utils.py:97 | Epoch 6 [Train]: loss 0.0022908325160805806, mse: 0.002253373386338353, rmse: 0.04746971019859246, mae 0.028448425233364105, r2: 0.9102768814663967, nrmse: 0.2041764088224961\n",
      "INFO logger 2023-08-13 23:36:39,910 | train_utils.py:99 | Epoch 6 [Test]: loss 3.9655967038529725e-06, mse: 0.00048088858602568507, rmse: 0.02192917203237927, mae 0.017009226605296135, r2: 0.9780625604872667, nrmse: 0.09517986928943767\n",
      "INFO logger 2023-08-13 23:36:39,911 | helpers.py:147 | Validation loss decreased (0.000004 --> 0.000004). Caching model ...\n",
      "INFO logger 2023-08-13 23:36:40,161 | train_utils.py:97 | Epoch 7 [Train]: loss 0.002265849985773317, mse: 0.0022137886844575405, rmse: 0.047050915872675, mae 0.02814989537000656, r2: 0.9118530344749175, nrmse: 0.20237509339117501\n",
      "INFO logger 2023-08-13 23:36:40,161 | train_utils.py:99 | Epoch 7 [Test]: loss 3.909170309842029e-06, mse: 0.0004736580012831837, rmse: 0.02176368537916278, mae 0.016871146857738495, r2: 0.9783924068704589, nrmse: 0.09446160240735771\n",
      "INFO logger 2023-08-13 23:36:40,162 | helpers.py:147 | Validation loss decreased (0.000004 --> 0.000004). Caching model ...\n",
      "INFO logger 2023-08-13 23:36:40,356 | train_utils.py:97 | Epoch 8 [Train]: loss 0.0022360322241766904, mse: 0.002171560423448682, rmse: 0.04660000454344057, mae 0.027839133515954018, r2: 0.9135344451584348, nrmse: 0.20043563651403584\n",
      "INFO logger 2023-08-13 23:36:40,357 | train_utils.py:99 | Epoch 8 [Test]: loss 3.834252016830308e-06, mse: 0.00046392035437747836, rmse: 0.0215388104215966, mae 0.01670195534825325, r2: 0.9788366263944184, nrmse: 0.09348557061573266\n",
      "INFO logger 2023-08-13 23:36:40,357 | helpers.py:147 | Validation loss decreased (0.000004 --> 0.000004). Caching model ...\n",
      "INFO logger 2023-08-13 23:36:40,557 | train_utils.py:97 | Epoch 9 [Train]: loss 0.0022028880417705155, mse: 0.0021390807814896107, rmse: 0.046250197637303245, mae 0.027564801275730133, r2: 0.9148276968846365, nrmse: 0.19893104932406505\n",
      "INFO logger 2023-08-13 23:36:40,558 | train_utils.py:99 | Epoch 9 [Test]: loss 3.7800257036840354e-06, mse: 0.0004571199242491275, rmse: 0.021380363052322743, mae 0.016480550169944763, r2: 0.9791468523912817, nrmse: 0.09279785655728699\n",
      "INFO logger 2023-08-13 23:36:40,558 | helpers.py:147 | Validation loss decreased (0.000004 --> 0.000004). Caching model ...\n",
      "INFO logger 2023-08-13 23:36:40,750 | train_utils.py:97 | Epoch 10 [Train]: loss 0.002175830529244521, mse: 0.002116082003340125, rmse: 0.04600089133201796, mae 0.0273456834256649, r2: 0.9157434498137513, nrmse: 0.19785873466494872\n",
      "INFO logger 2023-08-13 23:36:40,750 | train_utils.py:99 | Epoch 10 [Test]: loss 3.776235653847852e-06, mse: 0.00045693101128563285, rmse: 0.021375944687560194, mae 0.016350600868463516, r2: 0.9791554679009796, nrmse: 0.0927786793909101\n",
      "INFO logger 2023-08-13 23:36:40,751 | helpers.py:147 | Validation loss decreased (0.000004 --> 0.000004). Caching model ...\n",
      "INFO logger 2023-08-13 23:36:40,944 | train_utils.py:97 | Epoch 11 [Train]: loss 0.0021599119130629473, mse: 0.002098353113979101, rmse: 0.04580778442556572, mae 0.02718939818441868, r2: 0.9164493615990104, nrmse: 0.19702814449464243\n",
      "INFO logger 2023-08-13 23:36:40,945 | train_utils.py:99 | Epoch 11 [Test]: loss 3.8129444698609664e-06, mse: 0.0004618953389581293, rmse: 0.0214917504861314, mae 0.016322769224643707, r2: 0.9789290041225772, nrmse: 0.09328131491014864\n",
      "INFO logger 2023-08-13 23:36:40,945 | helpers.py:135 | EarlyStopping counter: 1 out of 50\n",
      "INFO logger 2023-08-13 23:36:41,135 | train_utils.py:97 | Epoch 12 [Train]: loss 0.002150263934312627, mse: 0.0020885642152279615, rmse: 0.045700811975587056, mae 0.02709254063665867, r2: 0.9168391232105886, nrmse: 0.19656803528840935\n",
      "INFO logger 2023-08-13 23:36:41,136 | train_utils.py:99 | Epoch 12 [Test]: loss 3.900219461934614e-06, mse: 0.0004733499081339687, rmse: 0.02175660608031429, mae 0.016381895169615746, r2: 0.978406462264228, nrmse: 0.09443087590577957\n",
      "INFO logger 2023-08-13 23:36:41,136 | helpers.py:135 | EarlyStopping counter: 2 out of 50\n",
      "INFO logger 2023-08-13 23:36:41,333 | train_utils.py:97 | Epoch 13 [Train]: loss 0.002146152338424984, mse: 0.0020802184008061886, rmse: 0.045609411318347316, mae 0.027033910155296326, r2: 0.9171714386401145, nrmse: 0.19617490337584548\n",
      "INFO logger 2023-08-13 23:36:41,333 | train_utils.py:99 | Epoch 13 [Test]: loss 4.000574277808377e-06, mse: 0.00048638248699717224, rmse: 0.022054080960157287, mae 0.016480060294270515, r2: 0.9778119358697991, nrmse: 0.0957220154042769\n",
      "INFO logger 2023-08-13 23:36:41,334 | helpers.py:135 | EarlyStopping counter: 3 out of 50\n",
      "INFO logger 2023-08-13 23:36:41,526 | train_utils.py:97 | Epoch 14 [Train]: loss 0.002143725722980423, mse: 0.002077612094581127, rmse: 0.04558083034106693, mae 0.027056768536567688, r2: 0.9172752123669271, nrmse: 0.19605197106222205\n",
      "INFO logger 2023-08-13 23:36:41,526 | train_utils.py:99 | Epoch 14 [Test]: loss 4.14709806057584e-06, mse: 0.0005050905747339129, rmse: 0.022474220225269508, mae 0.016656937077641487, r2: 0.976958501529016, nrmse: 0.09754555895976072\n",
      "INFO logger 2023-08-13 23:36:41,526 | helpers.py:135 | EarlyStopping counter: 4 out of 50\n",
      "INFO logger 2023-08-13 23:36:41,783 | train_utils.py:97 | Epoch 15 [Train]: loss 0.0021441752092542653, mse: 0.002074079355224967, rmse: 0.04554206138532782, mae 0.02708299085497856, r2: 0.9174158680896279, nrmse: 0.19588521828190184\n",
      "INFO logger 2023-08-13 23:36:41,784 | train_utils.py:99 | Epoch 15 [Test]: loss 4.284851186107934e-06, mse: 0.0005226569483056664, rmse: 0.022861691720117004, mae 0.016866881400346756, r2: 0.9761571467067127, nrmse: 0.09922731357313663\n",
      "INFO logger 2023-08-13 23:36:41,784 | helpers.py:135 | EarlyStopping counter: 5 out of 50\n",
      "INFO logger 2023-08-13 23:36:41,976 | train_utils.py:97 | Epoch 16 [Train]: loss 0.0021449903881428046, mse: 0.0020741703920066357, rmse: 0.045543060854609185, mae 0.027152832597494125, r2: 0.9174122559105515, nrmse: 0.19588951719267556\n",
      "INFO logger 2023-08-13 23:36:41,976 | train_utils.py:99 | Epoch 16 [Test]: loss 4.449754206526683e-06, mse: 0.0005435773055069149, rmse: 0.023314744380046607, mae 0.017175359651446342, r2: 0.97520279144469, nrmse: 0.10119371216263473\n",
      "INFO logger 2023-08-13 23:36:41,977 | helpers.py:135 | EarlyStopping counter: 6 out of 50\n",
      "INFO logger 2023-08-13 23:36:42,171 | train_utils.py:97 | Epoch 17 [Train]: loss 0.0021470334710708507, mse: 0.002071524038910866, rmse: 0.04551399827427674, mae 0.02718828245997429, r2: 0.9175176257215651, nrmse: 0.19576451341113635\n",
      "INFO logger 2023-08-13 23:36:42,172 | train_utils.py:99 | Epoch 17 [Test]: loss 4.578775198734253e-06, mse: 0.0005601134034805, rmse: 0.023666715096956316, mae 0.017435908317565918, r2: 0.9744484363434961, nrmse: 0.10272138164234472\n",
      "INFO logger 2023-08-13 23:36:42,172 | helpers.py:135 | EarlyStopping counter: 7 out of 50\n",
      "INFO logger 2023-08-13 23:36:42,369 | train_utils.py:97 | Epoch 18 [Train]: loss 0.002147352646063201, mse: 0.002067218068987131, rmse: 0.045466669869115454, mae 0.027201762422919273, r2: 0.9176890765185272, nrmse: 0.19556094478262145\n",
      "INFO logger 2023-08-13 23:36:42,369 | train_utils.py:99 | Epoch 18 [Test]: loss 4.678081756498179e-06, mse: 0.0005729851545765996, rmse: 0.023937108316933346, mae 0.017632408067584038, r2: 0.9738612495840304, nrmse: 0.10389497776791498\n",
      "INFO logger 2023-08-13 23:36:42,370 | helpers.py:135 | EarlyStopping counter: 8 out of 50\n",
      "INFO logger 2023-08-13 23:36:42,563 | train_utils.py:97 | Epoch 19 [Train]: loss 0.0021449949589366904, mse: 0.002059762831777334, rmse: 0.04538461007629496, mae 0.027176328003406525, r2: 0.9179859177416972, nrmse: 0.19520798973535633\n",
      "INFO logger 2023-08-13 23:36:42,564 | train_utils.py:99 | Epoch 19 [Test]: loss 4.729880880483466e-06, mse: 0.0005799655918963253, rmse: 0.024082474787619428, mae 0.017734814435243607, r2: 0.9735428098724401, nrmse: 0.10452591639425886\n",
      "INFO logger 2023-08-13 23:36:42,565 | helpers.py:135 | EarlyStopping counter: 9 out of 50\n",
      "INFO logger 2023-08-13 23:36:42,748 | train_utils.py:97 | Epoch 20 [Train]: loss 0.002139343067448443, mse: 0.0020506929140537977, rmse: 0.04528457699983293, mae 0.02712990716099739, r2: 0.9183470547528249, nrmse: 0.194777728117368\n",
      "INFO logger 2023-08-13 23:36:42,748 | train_utils.py:99 | Epoch 20 [Test]: loss 4.748354318938645e-06, mse: 0.0005827579880133271, rmse: 0.024140380858912045, mae 0.017771489918231964, r2: 0.9734154235611006, nrmse: 0.10477724792143911\n",
      "INFO logger 2023-08-13 23:36:42,749 | helpers.py:135 | EarlyStopping counter: 10 out of 50\n",
      "INFO logger 2023-08-13 23:36:42,942 | train_utils.py:97 | Epoch 21 [Train]: loss 0.0021310731431302163, mse: 0.0020410758443176746, rmse: 0.04517826738950571, mae 0.02707546576857567, r2: 0.9187299848380065, nrmse: 0.1943204699127334\n",
      "INFO logger 2023-08-13 23:36:42,943 | train_utils.py:99 | Epoch 21 [Test]: loss 4.744721978119966e-06, mse: 0.0005827259155921638, rmse: 0.024139716559896964, mae 0.01776537299156189, r2: 0.9734168854564454, nrmse: 0.10477436464370608\n",
      "INFO logger 2023-08-13 23:36:42,943 | helpers.py:135 | EarlyStopping counter: 11 out of 50\n",
      "INFO logger 2023-08-13 23:36:43,124 | train_utils.py:97 | Epoch 22 [Train]: loss 0.0021212705633400055, mse: 0.0020319095347076654, rmse: 0.04507670723009463, mae 0.02702214941382408, r2: 0.9190949678436016, nrmse: 0.1938836400154949\n",
      "INFO logger 2023-08-13 23:36:43,125 | train_utils.py:99 | Epoch 22 [Test]: loss 4.730746642110536e-06, mse: 0.0005813224124722183, rmse: 0.024110628620428343, mae 0.017737574875354767, r2: 0.9734809125323826, nrmse: 0.10464811335284865\n",
      "INFO logger 2023-08-13 23:36:43,125 | helpers.py:135 | EarlyStopping counter: 12 out of 50\n",
      "INFO logger 2023-08-13 23:36:43,374 | train_utils.py:97 | Epoch 23 [Train]: loss 0.0021109596978371534, mse: 0.002023395849391818, rmse: 0.04498217257305185, mae 0.026975532993674278, r2: 0.9194339489270726, nrmse: 0.1934770281633578\n",
      "INFO logger 2023-08-13 23:36:43,375 | train_utils.py:99 | Epoch 23 [Test]: loss 4.710709484370542e-06, mse: 0.0005790864815935493, rmse: 0.02406421579012184, mae 0.017693549394607544, r2: 0.9735829132936371, nrmse: 0.10444666629796655\n",
      "INFO logger 2023-08-13 23:36:43,375 | helpers.py:135 | EarlyStopping counter: 13 out of 50\n",
      "INFO logger 2023-08-13 23:36:43,559 | train_utils.py:97 | Epoch 24 [Train]: loss 0.002100631005118768, mse: 0.002015222329646349, rmse: 0.04489122775828646, mae 0.026927854865789413, r2: 0.919759398404427, nrmse: 0.19308585691748034\n",
      "INFO logger 2023-08-13 23:36:43,560 | train_utils.py:99 | Epoch 24 [Test]: loss 4.6837890738900305e-06, mse: 0.0005759251653216779, rmse: 0.023998440893559687, mae 0.01763227954506874, r2: 0.973727127697837, nrmse: 0.10416118146305951\n",
      "INFO logger 2023-08-13 23:36:43,560 | helpers.py:135 | EarlyStopping counter: 14 out of 50\n",
      "INFO logger 2023-08-13 23:36:43,747 | train_utils.py:97 | Epoch 25 [Train]: loss 0.002090351133702848, mse: 0.0020073724444955587, rmse: 0.04480371016439999, mae 0.026882149279117584, r2: 0.9200719559393727, nrmse: 0.19270942681176095\n",
      "INFO logger 2023-08-13 23:36:43,747 | train_utils.py:99 | Epoch 25 [Test]: loss 4.651931950772982e-06, mse: 0.0005720829358324409, rmse: 0.023918255284038608, mae 0.017558380961418152, r2: 0.9739024028459295, nrmse: 0.1038131493612619\n",
      "INFO logger 2023-08-13 23:36:43,748 | helpers.py:135 | EarlyStopping counter: 15 out of 50\n",
      "INFO logger 2023-08-13 23:36:43,934 | train_utils.py:97 | Epoch 26 [Train]: loss 0.0020801976079849573, mse: 0.002000204287469387, rmse: 0.04472364349501712, mae 0.026842456310987473, r2: 0.9203573767575696, nrmse: 0.19236504457406495\n",
      "INFO logger 2023-08-13 23:36:43,935 | train_utils.py:99 | Epoch 26 [Test]: loss 4.620053611385326e-06, mse: 0.0005681787733919919, rmse: 0.02383650086300403, mae 0.017483657225966454, r2: 0.9740805065468052, nrmse: 0.10345830809792486\n",
      "INFO logger 2023-08-13 23:36:43,936 | helpers.py:135 | EarlyStopping counter: 16 out of 50\n",
      "INFO logger 2023-08-13 23:36:44,117 | train_utils.py:97 | Epoch 27 [Train]: loss 0.0020704874224089184, mse: 0.0019939446356147528, rmse: 0.044653607196001005, mae 0.026810327544808388, r2: 0.92060662560264, nrmse: 0.19206380490016542\n",
      "INFO logger 2023-08-13 23:36:44,117 | train_utils.py:99 | Epoch 27 [Test]: loss 4.591311638553937e-06, mse: 0.0005646179197356105, rmse: 0.023761690170011274, mae 0.017415320500731468, r2: 0.9742429477343413, nrmse: 0.10313360491396566\n",
      "INFO logger 2023-08-13 23:36:44,118 | helpers.py:135 | EarlyStopping counter: 17 out of 50\n",
      "INFO logger 2023-08-13 23:36:44,303 | train_utils.py:97 | Epoch 28 [Train]: loss 0.0020615103779933774, mse: 0.0019885809160768986, rmse: 0.04459350755521367, mae 0.02678559347987175, r2: 0.9208201834406293, nrmse: 0.19180530471602392\n",
      "INFO logger 2023-08-13 23:36:44,303 | train_utils.py:99 | Epoch 28 [Test]: loss 4.566746951161843e-06, mse: 0.0005615410045720637, rmse: 0.023696856428059476, mae 0.017354873940348625, r2: 0.9743833127907632, nrmse: 0.10285220500176624\n",
      "INFO logger 2023-08-13 23:36:44,304 | helpers.py:135 | EarlyStopping counter: 18 out of 50\n",
      "INFO logger 2023-08-13 23:36:44,501 | train_utils.py:97 | Epoch 29 [Train]: loss 0.002053352103431303, mse: 0.0019838721491396427, rmse: 0.04454067971124422, mae 0.026766924187541008, r2: 0.9210076702494359, nrmse: 0.19157808193706882\n",
      "INFO logger 2023-08-13 23:36:44,501 | train_utils.py:99 | Epoch 29 [Test]: loss 4.544817243032337e-06, mse: 0.0005587644409388304, rmse: 0.023638198766801805, mae 0.017301321029663086, r2: 0.9745099749758473, nrmse: 0.10259761132522043\n",
      "INFO logger 2023-08-13 23:36:44,502 | helpers.py:135 | EarlyStopping counter: 19 out of 50\n",
      "INFO logger 2023-08-13 23:36:44,691 | train_utils.py:97 | Epoch 30 [Train]: loss 0.0020459438666689377, mse: 0.001979739172384143, rmse: 0.044494259993668205, mae 0.026753317564725876, r2: 0.9211722459362369, nrmse: 0.1913784217496867\n",
      "INFO logger 2023-08-13 23:36:44,692 | train_utils.py:99 | Epoch 30 [Test]: loss 4.525766325529647e-06, mse: 0.0005563198355957866, rmse: 0.02358643329534558, mae 0.017253698781132698, r2: 0.9746214975523365, nrmse: 0.10237293203502039\n",
      "INFO logger 2023-08-13 23:36:44,692 | helpers.py:135 | EarlyStopping counter: 20 out of 50\n",
      "INFO logger 2023-08-13 23:36:44,949 | train_utils.py:97 | Epoch 31 [Train]: loss 0.0020391692076093303, mse: 0.0019761526491492987, rmse: 0.04445393851110719, mae 0.02674497291445732, r2: 0.9213150481812414, nrmse: 0.19120499125109583\n",
      "INFO logger 2023-08-13 23:36:44,949 | train_utils.py:99 | Epoch 31 [Test]: loss 4.509834143567092e-06, mse: 0.0005542410654015839, rmse: 0.02354232497867583, mae 0.01721247099339962, r2: 0.9747163274798197, nrmse: 0.10218148733254803\n",
      "INFO logger 2023-08-13 23:36:44,950 | helpers.py:135 | EarlyStopping counter: 21 out of 50\n",
      "INFO logger 2023-08-13 23:36:45,143 | train_utils.py:97 | Epoch 32 [Train]: loss 0.0020330227577151395, mse: 0.001973126083612442, rmse: 0.04441988387662041, mae 0.026742378249764442, r2: 0.9214355548455624, nrmse: 0.19105851567869006\n",
      "INFO logger 2023-08-13 23:36:45,144 | train_utils.py:99 | Epoch 32 [Test]: loss 4.49762144519673e-06, mse: 0.0005526062450371683, rmse: 0.023507578459662073, mae 0.01717992313206196, r2: 0.9747909056985986, nrmse: 0.10203067593241358\n",
      "INFO logger 2023-08-13 23:36:45,144 | helpers.py:135 | EarlyStopping counter: 22 out of 50\n",
      "INFO logger 2023-08-13 23:36:45,336 | train_utils.py:97 | Epoch 33 [Train]: loss 0.0020274754780798387, mse: 0.0019705956801772118, rmse: 0.04439139196034758, mae 0.026745090261101723, r2: 0.9215363073284553, nrmse: 0.1909359664336932\n",
      "INFO logger 2023-08-13 23:36:45,337 | train_utils.py:99 | Epoch 33 [Test]: loss 4.488928721551806e-06, mse: 0.0005513938958756626, rmse: 0.023481777953887194, mae 0.01715489663183689, r2: 0.9748462107625185, nrmse: 0.10191869319254412\n",
      "INFO logger 2023-08-13 23:36:45,337 | helpers.py:135 | EarlyStopping counter: 23 out of 50\n",
      "INFO logger 2023-08-13 23:36:45,533 | train_utils.py:97 | Epoch 34 [Train]: loss 0.0020224814816640737, mse: 0.001968543278053403, rmse: 0.04436826881965762, mae 0.026752978563308716, r2: 0.9216180307217944, nrmse: 0.19083650933132157\n",
      "INFO logger 2023-08-13 23:36:45,533 | train_utils.py:99 | Epoch 34 [Test]: loss 4.483800303249435e-06, mse: 0.0005506127490662038, rmse: 0.02346513901655398, mae 0.017137454822659492, r2: 0.9748818433755984, nrmse: 0.10184647469390896\n",
      "INFO logger 2023-08-13 23:36:45,534 | helpers.py:135 | EarlyStopping counter: 24 out of 50\n",
      "INFO logger 2023-08-13 23:36:45,725 | train_utils.py:97 | Epoch 35 [Train]: loss 0.0020180089982787673, mse: 0.00196687295101583, rmse: 0.04434944138335713, mae 0.026765046641230583, r2: 0.9216845360426515, nrmse: 0.19075552888473601\n",
      "INFO logger 2023-08-13 23:36:45,725 | train_utils.py:99 | Epoch 35 [Test]: loss 4.481578303123082e-06, mse: 0.0005501831183210015, rmse: 0.023455982569933018, mae 0.01712614856660366, r2: 0.9749014441800014, nrmse: 0.1018067326830728\n",
      "INFO logger 2023-08-13 23:36:45,726 | helpers.py:135 | EarlyStopping counter: 25 out of 50\n",
      "INFO logger 2023-08-13 23:36:45,923 | train_utils.py:97 | Epoch 36 [Train]: loss 0.0020139743693363974, mse: 0.0019655153155326843, rmse: 0.04433413262411575, mae 0.02677980624139309, r2: 0.9217386047925253, nrmse: 0.19068968294903604\n",
      "INFO logger 2023-08-13 23:36:45,923 | train_utils.py:99 | Epoch 36 [Test]: loss 4.481780005321986e-06, mse: 0.0005500470288097858, rmse: 0.023453081435278092, mae 0.017120670527219772, r2: 0.9749076506958686, nrmse: 0.10179414079785074\n",
      "INFO logger 2023-08-13 23:36:45,924 | helpers.py:135 | EarlyStopping counter: 26 out of 50\n",
      "INFO logger 2023-08-13 23:36:46,114 | train_utils.py:97 | Epoch 37 [Train]: loss 0.0020103139052083953, mse: 0.001964518101885915, rmse: 0.04432288462956709, mae 0.026798289269208908, r2: 0.9217783056292996, nrmse: 0.19064130314802627\n",
      "INFO logger 2023-08-13 23:36:46,115 | train_utils.py:99 | Epoch 37 [Test]: loss 4.4848973067603326e-06, mse: 0.0005502679268829525, rmse: 0.023457790323961728, mae 0.01712149940431118, r2: 0.9748975739467025, nrmse: 0.10181457893426306\n",
      "INFO logger 2023-08-13 23:36:46,115 | helpers.py:135 | EarlyStopping counter: 27 out of 50\n",
      "INFO logger 2023-08-13 23:36:46,307 | train_utils.py:97 | Epoch 38 [Train]: loss 0.002007029738683158, mse: 0.0019638712983578444, rmse: 0.044315587532580955, mae 0.02682069130241871, r2: 0.9218040608714826, nrmse: 0.1906099169219207\n",
      "INFO logger 2023-08-13 23:36:46,308 | train_utils.py:99 | Epoch 38 [Test]: loss 4.4909033391192775e-06, mse: 0.0005508445901796222, rmse: 0.023470078614687726, mae 0.017129041254520416, r2: 0.9748712689127974, nrmse: 0.10186791418574287\n",
      "INFO logger 2023-08-13 23:36:46,309 | helpers.py:135 | EarlyStopping counter: 28 out of 50\n",
      "INFO logger 2023-08-13 23:36:46,569 | train_utils.py:97 | Epoch 39 [Train]: loss 0.0020040740447047236, mse: 0.0019634729251265526, rmse: 0.044311092574281584, mae 0.026845475658774376, r2: 0.9218199172501665, nrmse: 0.19059058323651304\n",
      "INFO logger 2023-08-13 23:36:46,569 | train_utils.py:99 | Epoch 39 [Test]: loss 4.498830856980333e-06, mse: 0.000551659963093698, rmse: 0.023487442668236533, mae 0.017141273245215416, r2: 0.9748340714155175, nrmse: 0.10194327992890316\n",
      "INFO logger 2023-08-13 23:36:46,570 | helpers.py:135 | EarlyStopping counter: 29 out of 50\n",
      "INFO logger 2023-08-13 23:36:46,765 | train_utils.py:97 | Epoch 40 [Train]: loss 0.002001427940110891, mse: 0.0019634126219898462, rmse: 0.04431041211712938, mae 0.026873989030718803, r2: 0.9218223091230913, nrmse: 0.19058765645863482\n",
      "INFO logger 2023-08-13 23:36:46,766 | train_utils.py:99 | Epoch 40 [Test]: loss 4.509505087505252e-06, mse: 0.0005528172478079796, rmse: 0.023512066004670443, mae 0.01715981960296631, r2: 0.9747812770164569, nrmse: 0.10205015336396897\n",
      "INFO logger 2023-08-13 23:36:46,766 | helpers.py:135 | EarlyStopping counter: 30 out of 50\n",
      "INFO logger 2023-08-13 23:36:46,964 | train_utils.py:97 | Epoch 41 [Train]: loss 0.0019990629815042783, mse: 0.001963519025593996, rmse: 0.04431161276227707, mae 0.02690366841852665, r2: 0.9218180777733322, nrmse: 0.19059282066573655\n",
      "INFO logger 2023-08-13 23:36:46,965 | train_utils.py:99 | Epoch 41 [Test]: loss 4.521334613560911e-06, mse: 0.0005541209247894585, rmse: 0.023539773252719717, mae 0.017181063070893288, r2: 0.9747218045273963, nrmse: 0.1021704119967986\n",
      "INFO logger 2023-08-13 23:36:46,965 | helpers.py:135 | EarlyStopping counter: 31 out of 50\n",
      "INFO logger 2023-08-13 23:36:47,163 | train_utils.py:97 | Epoch 42 [Train]: loss 0.0019969157374595315, mse: 0.0019638596568256617, rmse: 0.0443154561843344, mae 0.02693546749651432, r2: 0.9218045195304543, nrmse: 0.19060935196769674\n",
      "INFO logger 2023-08-13 23:36:47,164 | train_utils.py:99 | Epoch 42 [Test]: loss 4.534919423969003e-06, mse: 0.0005556468386203051, rmse: 0.02357216236623838, mae 0.017206454649567604, r2: 0.9746521968350109, nrmse: 0.10231099147634098\n",
      "INFO logger 2023-08-13 23:36:47,164 | helpers.py:135 | EarlyStopping counter: 32 out of 50\n",
      "INFO logger 2023-08-13 23:36:47,362 | train_utils.py:97 | Epoch 43 [Train]: loss 0.0019949924261219655, mse: 0.0019644403364509344, rmse: 0.04432200736035017, mae 0.026969555765390396, r2: 0.9217813943453945, nrmse: 0.1906375298433751\n",
      "INFO logger 2023-08-13 23:36:47,363 | train_utils.py:99 | Epoch 43 [Test]: loss 4.550235167118734e-06, mse: 0.0005573930102400482, rmse: 0.02360917216337854, mae 0.017235886305570602, r2: 0.9745725361676388, nrmse: 0.10247162625311358\n",
      "INFO logger 2023-08-13 23:36:47,363 | helpers.py:135 | EarlyStopping counter: 33 out of 50\n",
      "INFO logger 2023-08-13 23:36:47,562 | train_utils.py:97 | Epoch 44 [Train]: loss 0.0019932851346078305, mse: 0.001965251751244068, rmse: 0.04433116004848134, mae 0.02700597047805786, r2: 0.9217490937350068, nrmse: 0.19067689732604723\n",
      "INFO logger 2023-08-13 23:36:47,562 | train_utils.py:99 | Epoch 44 [Test]: loss 4.567145364323028e-06, mse: 0.0005593445966951549, rmse: 0.023650467155960257, mae 0.017268823459744453, r2: 0.9744835099387722, nrmse: 0.10265086019730593\n",
      "INFO logger 2023-08-13 23:36:47,562 | helpers.py:135 | EarlyStopping counter: 34 out of 50\n",
      "INFO logger 2023-08-13 23:36:47,760 | train_utils.py:97 | Epoch 45 [Train]: loss 0.001991796442710508, mse: 0.0019662713166326284, rmse: 0.044342657978887874, mae 0.027044080197811127, r2: 0.9217084968513772, nrmse: 0.19072635214954364\n",
      "INFO logger 2023-08-13 23:36:47,761 | train_utils.py:99 | Epoch 45 [Test]: loss 4.58534310373887e-06, mse: 0.0005614645779132843, rmse: 0.023695243782524886, mae 0.017304712906479836, r2: 0.9743868000559921, nrmse: 0.10284520558605799\n",
      "INFO logger 2023-08-13 23:36:47,761 | helpers.py:135 | EarlyStopping counter: 35 out of 50\n",
      "INFO logger 2023-08-13 23:36:47,961 | train_utils.py:97 | Epoch 46 [Train]: loss 0.001990496154832773, mse: 0.001967374701052904, rmse: 0.04435509780231472, mae 0.027082014828920364, r2: 0.9216645697628774, nrmse: 0.1907798582371741\n",
      "INFO logger 2023-08-13 23:36:47,962 | train_utils.py:99 | Epoch 46 [Test]: loss 4.603755016415267e-06, mse: 0.0005636190762743354, rmse: 0.023740662928282676, mae 0.01734125427901745, r2: 0.9742885138506056, nrmse: 0.10304233972090272\n",
      "INFO logger 2023-08-13 23:36:47,962 | helpers.py:135 | EarlyStopping counter: 36 out of 50\n",
      "INFO logger 2023-08-13 23:36:48,217 | train_utils.py:97 | Epoch 47 [Train]: loss 0.0019893381261051847, mse: 0.0019686168525367975, rmse: 0.044369097945944284, mae 0.027120761573314667, r2: 0.9216151038468923, nrmse: 0.19084007556391416\n",
      "INFO logger 2023-08-13 23:36:48,218 | train_utils.py:99 | Epoch 47 [Test]: loss 4.6227686472714015e-06, mse: 0.0005658571026287973, rmse: 0.023787751104902652, mae 0.017379721626639366, r2: 0.9741864188321961, nrmse: 0.1032467180024516\n",
      "INFO logger 2023-08-13 23:36:48,218 | helpers.py:135 | EarlyStopping counter: 37 out of 50\n",
      "INFO logger 2023-08-13 23:36:48,413 | train_utils.py:97 | Epoch 48 [Train]: loss 0.001988318946528941, mse: 0.001970065524801612, rmse: 0.04438542018277637, mae 0.027161123231053352, r2: 0.9215574158641799, nrmse: 0.1909102806628369\n",
      "INFO logger 2023-08-13 23:36:48,413 | train_utils.py:99 | Epoch 48 [Test]: loss 4.643067739485506e-06, mse: 0.0005682632909156382, rmse: 0.02383827365636275, mae 0.017421342432498932, r2: 0.9740766536378226, nrmse: 0.1034660026082288\n",
      "INFO logger 2023-08-13 23:36:48,414 | helpers.py:135 | EarlyStopping counter: 38 out of 50\n",
      "INFO logger 2023-08-13 23:36:48,612 | train_utils.py:97 | Epoch 49 [Train]: loss 0.0019874983886555317, mse: 0.0019717856775969267, rmse: 0.04440479340788477, mae 0.027203723788261414, r2: 0.9214889297699275, nrmse: 0.1909936086526939\n",
      "INFO logger 2023-08-13 23:36:48,612 | train_utils.py:99 | Epoch 49 [Test]: loss 4.664938846279098e-06, mse: 0.0005708775133825839, rmse: 0.023893043200534, mae 0.017466507852077484, r2: 0.9739573960243925, nrmse: 0.10370372056892356\n",
      "INFO logger 2023-08-13 23:36:48,612 | helpers.py:135 | EarlyStopping counter: 39 out of 50\n",
      "INFO logger 2023-08-13 23:36:48,800 | train_utils.py:97 | Epoch 50 [Train]: loss 0.0019868852470655185, mse: 0.0019736450631171465, rmse: 0.04442572524019328, mae 0.027247080579400063, r2: 0.9214148969811945, nrmse: 0.19108364051370477\n",
      "INFO logger 2023-08-13 23:36:48,801 | train_utils.py:99 | Epoch 50 [Test]: loss 4.687233744211639e-06, mse: 0.0005735550657846034, rmse: 0.023949009703630824, mae 0.017512286081910133, r2: 0.9738352490647344, nrmse: 0.10394663372777321\n",
      "INFO logger 2023-08-13 23:36:48,801 | helpers.py:135 | EarlyStopping counter: 40 out of 50\n",
      "INFO logger 2023-08-13 23:36:48,995 | train_utils.py:97 | Epoch 51 [Train]: loss 0.0019864265296536568, mse: 0.0019754767417907715, rmse: 0.04444633552713622, mae 0.027288885787129402, r2: 0.921341952967504, nrmse: 0.19117228934588018\n",
      "INFO logger 2023-08-13 23:36:48,996 | train_utils.py:99 | Epoch 51 [Test]: loss 4.7083558421357025e-06, mse: 0.0005760974017903209, rmse: 0.024002029118187506, mae 0.01755584217607975, r2: 0.9737192718878933, nrmse: 0.1041767555463196\n",
      "INFO logger 2023-08-13 23:36:48,996 | helpers.py:135 | EarlyStopping counter: 41 out of 50\n",
      "INFO logger 2023-08-13 23:36:49,199 | train_utils.py:97 | Epoch 52 [Train]: loss 0.001986071867390703, mse: 0.0019774651154875755, rmse: 0.044468698153730286, mae 0.027331462129950523, r2: 0.921262787286337, nrmse: 0.19126847532996802\n",
      "INFO logger 2023-08-13 23:36:49,200 | train_utils.py:99 | Epoch 52 [Test]: loss 4.7299601075774874e-06, mse: 0.0005787102272734046, rmse: 0.024056396805702315, mae 0.017600400373339653, r2: 0.9736000765702227, nrmse: 0.10441272931603547\n",
      "INFO logger 2023-08-13 23:36:49,200 | helpers.py:135 | EarlyStopping counter: 42 out of 50\n",
      "INFO logger 2023-08-13 23:36:49,383 | train_utils.py:97 | Epoch 53 [Train]: loss 0.0019858758818232813, mse: 0.0019796290434896946, rmse: 0.044493022413516645, mae 0.027375170961022377, r2: 0.9211766231842393, nrmse: 0.1913730986779865\n",
      "INFO logger 2023-08-13 23:36:49,384 | train_utils.py:99 | Epoch 53 [Test]: loss 4.752083855303193e-06, mse: 0.000581399945076555, rmse: 0.024112236417979875, mae 0.01764574460685253, r2: 0.9734773769958205, nrmse: 0.10465509172670485\n",
      "INFO logger 2023-08-13 23:36:49,385 | helpers.py:135 | EarlyStopping counter: 43 out of 50\n",
      "INFO logger 2023-08-13 23:36:49,578 | train_utils.py:97 | Epoch 54 [Train]: loss 0.0019858738785096183, mse: 0.0019820532761514187, rmse: 0.04452025691919824, mae 0.027421146631240845, r2: 0.9210800954127991, nrmse: 0.19149023955672498\n",
      "INFO logger 2023-08-13 23:36:49,579 | train_utils.py:99 | Epoch 54 [Test]: loss 4.775461851583191e-06, mse: 0.0005842578248120844, rmse: 0.024171425791874264, mae 0.017693568021059036, r2: 0.9733470058658942, nrmse: 0.10491199321219064\n",
      "INFO logger 2023-08-13 23:36:49,579 | helpers.py:135 | EarlyStopping counter: 44 out of 50\n",
      "INFO logger 2023-08-13 23:36:49,763 | train_utils.py:97 | Epoch 55 [Train]: loss 0.001986068315977135, mse: 0.001984512200579047, rmse: 0.044547864152830574, mae 0.02746654488146305, r2: 0.920982191300942, nrmse: 0.1916089836105014\n",
      "INFO logger 2023-08-13 23:36:49,764 | train_utils.py:99 | Epoch 55 [Test]: loss 4.797842008273166e-06, mse: 0.00058700330555439, rmse: 0.024228151096490833, mae 0.017738454043865204, r2: 0.9732217615817473, nrmse: 0.1051581998209416\n",
      "INFO logger 2023-08-13 23:36:49,765 | helpers.py:135 | EarlyStopping counter: 45 out of 50\n",
      "INFO logger 2023-08-13 23:36:50,000 | train_utils.py:97 | Epoch 56 [Train]: loss 0.001986406891284367, mse: 0.00198694528080523, rmse: 0.044575164394595676, mae 0.02751048095524311, r2: 0.9208853068029765, nrmse: 0.1917264072328548\n",
      "INFO logger 2023-08-13 23:36:50,000 | train_utils.py:99 | Epoch 56 [Test]: loss 4.81865885341315e-06, mse: 0.0005895659560337663, rmse: 0.02428097930549273, mae 0.017780236899852753, r2: 0.9731048543597332, nrmse: 0.1053874917440553\n",
      "INFO logger 2023-08-13 23:36:50,001 | helpers.py:135 | EarlyStopping counter: 46 out of 50\n",
      "INFO logger 2023-08-13 23:36:50,195 | train_utils.py:97 | Epoch 57 [Train]: loss 0.0019868307281285524, mse: 0.0019893688149750233, rmse: 0.04460234091362272, mae 0.027552930638194084, r2: 0.920788808793367, nrmse: 0.19184329869977182\n",
      "INFO logger 2023-08-13 23:36:50,196 | train_utils.py:99 | Epoch 57 [Test]: loss 4.838119305395814e-06, mse: 0.0005919696995988488, rmse: 0.02433042744381711, mae 0.01781911961734295, r2: 0.9729952012936283, nrmse: 0.10560211304099125\n",
      "INFO logger 2023-08-13 23:36:50,196 | helpers.py:135 | EarlyStopping counter: 47 out of 50\n",
      "INFO logger 2023-08-13 23:36:50,378 | train_utils.py:97 | Epoch 58 [Train]: loss 0.0019873422619032985, mse: 0.0019919774495065212, rmse: 0.04463157458018394, mae 0.02759643644094467, r2: 0.9206849454339854, nrmse: 0.19196903835628568\n",
      "INFO logger 2023-08-13 23:36:50,378 | train_utils.py:99 | Epoch 58 [Test]: loss 4.857979829598596e-06, mse: 0.0005944349686615169, rmse: 0.024381037071082863, mae 0.017858855426311493, r2: 0.972882736772612, nrmse: 0.10582177558460343\n",
      "INFO logger 2023-08-13 23:36:50,379 | helpers.py:135 | EarlyStopping counter: 48 out of 50\n",
      "INFO logger 2023-08-13 23:36:50,567 | train_utils.py:97 | Epoch 59 [Train]: loss 0.0019880467645215015, mse: 0.0019947693217545748, rmse: 0.044662840502531574, mae 0.027640899643301964, r2: 0.9205737807572172, nrmse: 0.1921035191381728\n",
      "INFO logger 2023-08-13 23:36:50,567 | train_utils.py:99 | Epoch 59 [Test]: loss 4.877981064494717e-06, mse: 0.0005969301564618945, rmse: 0.024432154151075063, mae 0.01789943315088749, r2: 0.9727689109782561, nrmse: 0.10604364064111042\n",
      "INFO logger 2023-08-13 23:36:50,568 | helpers.py:135 | EarlyStopping counter: 49 out of 50\n",
      "INFO logger 2023-08-13 23:36:50,749 | train_utils.py:97 | Epoch 60 [Train]: loss 0.001988923248017016, mse: 0.0019974177703261375, rmse: 0.04469248001986618, mae 0.027682041749358177, r2: 0.92046832973843, nrmse: 0.19223100443739402\n",
      "INFO logger 2023-08-13 23:36:50,750 | train_utils.py:99 | Epoch 60 [Test]: loss 4.895188191068777e-06, mse: 0.0005990866920910776, rmse: 0.024476247508371816, mae 0.017933262512087822, r2: 0.972670535934398, nrmse: 0.10623502041494962\n",
      "INFO logger 2023-08-13 23:36:50,750 | helpers.py:135 | EarlyStopping counter: 50 out of 50\n",
      "INFO logger 2023-08-13 23:36:50,751 | train_utils.py:117 | Early Stopping\n"
     ]
    },
    {
     "data": {
      "text/plain": "<Figure size 640x480 with 1 Axes>",
      "image/png": "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xo0bh6NHj6K0tBQFBQUhPz9baropPj4earW6XU/3hoYGGI1GhWrVe3iuEa9fe4888gh27tyJDz74AEOHDpXWG41G2Gw2nDt3zq/8lXzNtFotRo4ciYyMDJSUlCAtLQ2//e1vea0CqKqqwunTp3HjjTdCo9FAo9Fg7969WLt2LTQaDRISEnjNLiE2NhbXXHMNvvjiC/431oHExESMHj3ab911110n3bYL9e9+hppu0mq1yMjIQEVFhbTO6XSioqIC2dnZCtasd0hJSYHRaPS7fhaLBQcOHLhir58oinjkkUfw7rvvYs+ePUhJSfHbnpGRgYiICL9rdvz4cdTW1l6x16wtp9OJlpYWXqsAbr75Zvz73/9GdXW1tGRmZuK+++6T3vOade7ChQv48ssvkZiYyP/GOjB58uR2Q1F8/vnnGD58OIAw/O7vcVfjK1hZWZmo0+nEzZs3i5999pk4b948MTY2VjSZTEpX7bJw/vx58ciRI+KRI0dEAOJLL70kHjlyRPz6669FURTF559/XoyNjRX/9Kc/if/617/EO++8U0xJSREvXryocM2V8fOf/1w0GAzihx9+KJ46dUpampqapDIPP/ywOGzYMHHPnj3ioUOHxOzsbDE7O1vBWitn8eLF4t69e8WamhrxX//6l7h48WJREATx/fffF0WR16orfJ9+EkVes7Yef/xx8cMPPxRramrEjz76SMzJyRHj4+PF06dPi6LI6xXIwYMHRY1GI/76178WT5w4If7hD38Qo6KixN///vdSmVD+7meo6aF169aJw4YNE7VarThhwgTx448/VrpKl40PPvhABNBuKSgoEEXR9WjfsmXLxISEBFGn04k333yzePz4cWUrraBA1wqAuGnTJqnMxYsXxf/5n/8R4+LixKioKPGuu+4ST506pVylFTRnzhxx+PDholarFQcNGiTefPPNUqARRV6rrmgbanjN/OXn54uJiYmiVqsVhwwZIubn54tffPGFtJ3XK7A///nP4pgxY0SdTieOGjVK3Lhxo9/2UP7uF0RRFHve3kNERESkLPapISIioj6BoYaIiIj6BIYaIiIi6hMYaoiIiKhPYKghIiKiPoGhhoiIiPoEhhoiIiLqExhqiIiIqE9gqCEiIqI+gaGGiIiI+gSGGiIiIuoTGGqIiIioT/j/EAo450bSFKsAAAAASUVORK5CYII="
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": "<Figure size 640x480 with 1 Axes>",
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAiwAAAGdCAYAAAAxCSikAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAA9hAAAPYQGoP6dpAABHoUlEQVR4nO3deXxU9b3/8feZmcwkIRtrwh4UEBEEZElBr/TepgW1KrZaSq1QtLZaUCitrVgFrVdjW+HiglJrhfZnvSBttWqVilG41xpq2a4iihsCCkmgQgLZZju/P87MZCaZkEwySwiv5+NxHjNz5jtnvnPEzHs+53u+xzBN0xQAAEAnZkt1BwAAAFpDYAEAAJ0egQUAAHR6BBYAANDpEVgAAECnR2ABAACdHoEFAAB0egQWAADQ6TlS3YF48Pv9OnjwoLKzs2UYRqq7AwAA2sA0TR0/flz9+vWTzXbyGkqXCCwHDx7UwIEDU90NAADQDgcOHNCAAQNO2qZLBJbs7GxJ1gfOyclJcW8AAEBbVFdXa+DAgaHv8ZPpEoEleBgoJyeHwAIAwCmmLcM5GHQLAAA6PQILAADo9NoVWFauXKnCwkKlp6erqKhIb775Zott33nnHX39619XYWGhDMPQihUrmrUpKSnRxIkTlZ2drT59+mjGjBnas2dPe7oGAAC6oJjHsKxbt06LFi3SqlWrVFRUpBUrVmjatGnas2eP+vTp06x9bW2tzjjjDF111VX64Q9/GHWbmzdv1rx58zRx4kR5vV7ddttt+spXvqLdu3erW7dusX8qAECn5fP55PF4Ut0NJIndbpfD4ejwtCOGaZpmLC8oKirSxIkT9fDDD0uy5kAZOHCgbrrpJt16660nfW1hYaEWLlyohQsXnrTd4cOH1adPH23evFkXXnhhq32qrq5Wbm6uqqqqGHQLAJ3YiRMn9OmnnyrGrx6c4jIzM9W3b185nc6I9bF8f8dUYXG73dq2bZsWL14cWmez2VRcXKyysrJYNnVSVVVVkqQePXpEfb6hoUENDQ2hx9XV1XF7bwBAYvh8Pn366afKzMxU7969mejzNGCaptxutw4fPqy9e/dq2LBhrU4Q15KYAsuRI0fk8/mUn58fsT4/P1/vvfdeuzrQlN/v18KFC3X++edr1KhRUduUlJTorrvuisv7AQCSw+PxyDRN9e7dWxkZGanuDpIkIyNDaWlp2rdvn9xut9LT09u1nU53ltC8efO0a9curV27tsU2ixcvVlVVVWg5cOBAEnsIAOgIKiunn/ZWVcLFVGHp1auX7Ha7KioqItZXVFSooKCgw52ZP3++XnjhBf3P//zPSafodblccrlcHX4/AABwaogp8jidTo0fP16lpaWhdX6/X6WlpZo8eXK7O2GapubPn69nnnlGr776qoYMGdLubQEAgK4n5hrNokWL9Jvf/Ea/+93v9O677+rGG29UTU2N5s6dK0maPXt2xKBct9utnTt3aufOnXK73frss8+0c+dOffjhh6E28+bN05NPPqmnnnpK2dnZKi8vV3l5uerq6uLwEQEA6FwKCwujzkuGkzDb4aGHHjIHDRpkOp1Oc9KkSeaWLVtCz02dOtWcM2dO6PHevXtNSc2WqVOnhtpEe16SuXr16jb1p6qqypRkVlVVtefjAACSoK6uzty9e7dZV1eX6q60WUvfT8Fl6dKl7dpuZWWlWVNT06G+TZ06NdQPl8tlDhs2zLz33ntNv98fahP8DrbZbOann34a8fqDBw+adrvdlGTu3bs3tP7Pf/6zWVRUZObk5JhZWVnmyJEjzQULFoSeX716ddR94XK5WuxrS//tY/n+btfFD+fPn6/58+dHfW7Tpk0RjwsLC1s9376151Ol3uPTspf3qM7j09JLz1GavdONUQYAJNChQ4dC99etW6clS5ZEzMSelZUVum+apnw+nxyO1r9ae/fuHZf+XX/99fr5z3+uhoYGvfrqq/re976nvLw83XjjjRHt+vfvr9///vcRR0B+97vfqX///tq/f39oXWlpqWbOnKl77rlHl112mQzD0O7du7Vx48aI7eXk5DSbkT7Rg6n5Bj4Jw5B+87979eSW/arz+FLdHQDoUkzTVK3bm5KlrT+UCwoKQktubq4Mwwg9fu+995Sdna2XXnpJ48ePl8vl0uuvv66PPvpIl19+ufLz85WVlaWJEyfqlVdeidhu00NChmHo8ccf1xVXXKHMzEwNGzZMzz33XKv9y8zMVEFBgQYPHqy5c+fq3HPPbRYuJGnOnDlavXp1xLrVq1drzpw5Eeuef/55nX/++brlllt01llnafjw4ZoxY4ZWrlwZ0S58PwSXplOexFu7KiynC6fdJpsh+U2p3u1TTnpaqrsEAF1GncenkUv+lpL33v3zacp0xucr8NZbb9X999+vM844Q927d9eBAwd08cUX65577pHL5dLvf/97XXrppdqzZ48GDRrU4nbuuusu/fKXv9SvfvUrPfTQQ7r66qu1b9++FidRDWeapl5//XW99957GjZsWLPnL7vsMq1atUqvv/66LrjgAr3++us6evSoLr30Ut19992hdgUFBXrqqae0a9euFudCSxUqLCdhGIbS0+ySpHqPP8W9AQB0Rj//+c/15S9/WWeeeaZ69OihMWPG6Pvf/75GjRqlYcOG6e6779aZZ57ZasXkO9/5jmbNmqWhQ4fq3nvv1YkTJ056cWFJeuSRR5SVlSWXy6ULL7xQfr9fN998c7N2aWlp+va3v60nnnhCkvTEE0/o29/+ttLSIn+I33TTTZo4caJGjx6twsJCffOb39QTTzwRMbu8ZM1In5WVFbFcdNFFbdld7UaFpRUZaXbVun0cEgKAOMtIs2v3z6el7L3jZcKECRGPT5w4oTvvvFN//etfdejQIXm9XtXV1UWMFYnm3HPPDd3v1q2bcnJyVFlZedLXXH311frZz36mo0ePaunSpZoyZYqmTJkSte21116rKVOm6N5779X69etVVlYmr9cb0aZbt27661//qo8++kivvfaatmzZoh/96Ed64IEHVFZWpszMTElSdna2tm/fHvHaRM9eTGBpRWOFhcACAPFkGEbcDsukUrdu3SIe//jHP9bGjRt1//33a+jQocrIyNCVV14pt9t90u00rXYYhiG//+TV/dzcXA0dOlSS9PTTT2vo0KH6whe+oOLi4mZtR48erREjRmjWrFk6++yzNWrUKO3cuTPqds8880ydeeaZ+u53v6uf/exnGj58uNatWxeawsRms4XeN1k4JNSK9DRrF1FhAQC0xd///nd95zvf0RVXXKHRo0eroKBAn3zyScLfNysrSwsWLNCPf/zjFgcVX3vttdq0aZOuvfbaNm+3sLBQmZmZqqmpiVdX2+XUj7YJFqywEFgAAG0xbNgw/fnPf9all14qwzB0xx13tFopiZfvf//7uvvuu/WnP/1JV155ZbPnr7/+el111VXKy8uL+vo777xTtbW1uvjiizV48GAdO3ZMDz74oDwej7785S+H2pmmqfLy8mav79OnT1yuGxQNFZZWBI9zNhBYAABtsHz5cnXv3l1TpkzRpZdeqmnTpum8885Lynv36NFDs2fP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"
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO logger 2023-08-13 23:36:50,907 | train_utils.py:140 | Best Loss: 3.776235653847852e-06, Best epoch: 60\n"
     ]
    }
   ],
   "source": [
    "trained_model = fit(model, X_train, y_train, X_val, y_val)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 90,
   "outputs": [],
   "source": [
    "# def transform_preds(y_pred_train, y_pred_val):\n",
    "#     if not isinstance(y_pred_train, np.ndarray):\n",
    "#         y_pred_train = y_pred_train.cpu().numpy()\n",
    "#     if not isinstance(y_pred_val, np.ndarray):\n",
    "#         y_pred_val = y_pred_val.cpu().numpy()\n",
    "#     return y_pred_train, y_pred_val\n",
    "#\n",
    "#\n",
    "# def round_predictions(y_pred_train, y_pred_val, dims):\n",
    "#     # round to closest integer\n",
    "#     if dims is None or len(dims) == 0:\n",
    "#         return y_pred_train, y_pred_val\n",
    "#     for dim in dims:\n",
    "#         y_pred_train[:, dim] = np.rint(y_pred_train[:, dim])\n",
    "#         y_pred_val[:, dim] = np.rint(y_pred_val[:, dim])\n",
    "#     return y_pred_train, y_pred_val\n",
    "#\n",
    "#\n",
    "# def inverse_transform(y_train, y_val, y_pred_train, y_pred_val,\n",
    "#                       y_scaler=None,\n",
    "#                       round_preds=False, dims=None):\n",
    "#     y_pred_train, y_pred_val = transform_preds(y_pred_train, y_pred_val)\n",
    "#\n",
    "#     if y_scaler is not None:\n",
    "#         y_train = y_scaler.inverse_transform(y_train)\n",
    "#         y_val = y_scaler.inverse_transform(y_val)\n",
    "#         y_pred_train = y_scaler.inverse_transform(y_pred_train)\n",
    "#         y_pred_val = y_scaler.inverse_transform(y_pred_val)\n",
    "#\n",
    "#     # to zeroes\n",
    "#     y_pred_train[y_pred_train < 0.] = 0.\n",
    "#     y_pred_val[y_pred_val < 0.] = 0.\n",
    "#\n",
    "#     if round_preds:\n",
    "#         y_pred_train, y_pred_val = round_predictions(y_pred_train, y_pred_val, dims)\n",
    "#\n",
    "#     return y_train, y_val, y_pred_train, y_pred_val\n",
    "#\n",
    "#\n",
    "# def make_plot(y_true, y_pred,\n",
    "#               title,\n",
    "#               feature_names=None,\n",
    "#               client=None):\n",
    "#     if feature_names is None:\n",
    "#         feature_names = [f\"feature_{i}\" for i in range(y_pred.shape[1])]\n",
    "#     assert len(feature_names) == y_pred.shape[1]\n",
    "#\n",
    "#     for i in range(y_pred.shape[1]):\n",
    "#         plt.figure(figsize=(8, 6))\n",
    "#         plt.ticklabel_format(style='plain')\n",
    "#         plt.plot(y_true[:, i], label=\"Actual\")\n",
    "#         plt.plot(y_pred[:, i], label=\"Predicted\")\n",
    "#         if client is not None:\n",
    "#             plt.title(f\"[{client} {title}] {feature_names[i]} prediction\")\n",
    "#         else:\n",
    "#             plt.title(f\"[{title}] {feature_names[i]} prediction\")\n",
    "#         plt.legend()\n",
    "#         plt.show()\n",
    "#         plt.close()\n",
    "#\n",
    "#\n",
    "# ### During the inference stage we inverse the transformations we applied during preprocessing\n",
    "# def inference(\n",
    "#         model,  # the global model\n",
    "#         client_X_train,  # train data per client\n",
    "#         client_y_train,\n",
    "#         client_X_val,  # val data per client\n",
    "#         client_y_val,\n",
    "#         exogenous_data_train,  # exogenous data per client\n",
    "#         exogenous_data_val,\n",
    "#         y_scalers,  # the scalers used to transform the targets\n",
    "#         idxs=[1],\n",
    "#         apply_round=True,  # round to closest integer\n",
    "#         round_dimensions=[0],  # the dimensions to apply rounding\n",
    "#         plot=True,  # plot predictions\n",
    "# ):\n",
    "#     # load per client data to torch\n",
    "#     train_loaders, val_loaders = [], []\n",
    "#\n",
    "#     # get data per client\n",
    "#     for client in client_X_train:\n",
    "#         if client == \"all\":\n",
    "#             continue\n",
    "#         assert client in list(y_scalers.keys())\n",
    "#         if exogenous_data_train is not None:\n",
    "#             tmp_exogenous_data_train = exogenous_data_train[client]\n",
    "#             tmp_exogenous_data_val = exogenous_data_val[client]\n",
    "#         else:\n",
    "#             tmp_exogenous_data_train = None\n",
    "#             tmp_exogenous_data_val = None\n",
    "#\n",
    "#         num_features = len(client_X_train[client][0][0])\n",
    "#\n",
    "#         # to torch loader\n",
    "#         train_loaders.append(\n",
    "#             to_torch_dataset(\n",
    "#                 client_X_train[client], client_y_train[client],\n",
    "#                 num_lags=args.num_lags,\n",
    "#                 num_features=num_features,\n",
    "#                 exogenous_data=tmp_exogenous_data_train,\n",
    "#                 indices=idxs,\n",
    "#                 batch_size=1,\n",
    "#                 shuffle=False\n",
    "#             )\n",
    "#         )\n",
    "#         val_loaders.append(\n",
    "#             to_torch_dataset(\n",
    "#                 client_X_val[client], client_y_val[client],\n",
    "#                 num_lags=args.num_lags,\n",
    "#                 exogenous_data=tmp_exogenous_data_val,\n",
    "#                 indices=idxs,\n",
    "#                 batch_size=1,\n",
    "#                 shuffle=False\n",
    "#             )\n",
    "#\n",
    "#         )\n",
    "#\n",
    "#     # get client ids\n",
    "#     cids = [k for k in client_X_train.keys() if k != \"all\"]\n",
    "#\n",
    "#     # predict per client using the global model\n",
    "#     y_preds_train, y_preds_val = dict(), dict()\n",
    "#     for cid, train_loader, val_loader in zip(cids, train_loaders, val_loaders):\n",
    "#         print(f\"Prediction on {cid}\")\n",
    "#         train_mse, train_rmse, train_mae, train_r2, train_nrmse, y_pred_train = test(\n",
    "#             model, train_loader, None, device=device\n",
    "#         )\n",
    "#         val_mse, val_rmse, val_mae, val_r2, val_nrmse, y_pred_val = test(\n",
    "#             model, val_loader, None, device=device\n",
    "#         )\n",
    "#         y_preds_train[cid] = y_pred_train\n",
    "#         y_preds_val[cid] = y_pred_val\n",
    "#\n",
    "#     # 计算平均预测效果\n",
    "#     val_mse_avg, val_rmse_avg, val_mae_avg, val_r2_avg, val_nrmse_avg = 0, 0, 0, 0, 0\n",
    "#     for cid in cids:\n",
    "#         y_train, y_val = client_y_train[cid], client_y_val[cid]\n",
    "#         y_pred_train, y_pred_val = y_preds_train[cid], y_preds_val[cid]\n",
    "#\n",
    "#         y_scaler = y_scalers[cid]\n",
    "#         y_train, y_val, y_pred_train, y_pred_val = inverse_transform(\n",
    "#             y_train, y_val, y_pred_train, y_pred_val,\n",
    "#             y_scaler, round_preds=apply_round, dims=round_dimensions\n",
    "#         )\n",
    "#         train_mse, train_rmse, train_mae, train_r2, train_nrmse, train_res_per_dim = accumulate_metric(\n",
    "#             y_train, y_pred_train, True, return_all=True\n",
    "#         )\n",
    "#         val_mse, val_rmse, val_mae, val_r2, val_nrmse, val_res_per_dim = accumulate_metric(\n",
    "#             y_val, y_pred_val, True, return_all=True\n",
    "#         )\n",
    "#\n",
    "#         print(f\"\\nFinal Prediction on {cid} (Inference Stage)\")\n",
    "#         print(f\"[Train]: mse: {train_mse}, \"\n",
    "#               f\"rmse: {train_rmse}, mae {train_mae}, r2: {train_r2}, nrmse: {train_nrmse}\")\n",
    "#         print(f\"[Val]: mse: {val_mse}, \"\n",
    "#               f\"rmse: {val_rmse}, mae {val_mae}, r2: {val_r2}, nrmse: {val_nrmse}\\n\\n\")\n",
    "#         val_mse_avg = val_mse_avg + val_mse\n",
    "#         val_rmse_avg = val_rmse_avg + val_rmse\n",
    "#         val_mae_avg = val_mae_avg + val_mae\n",
    "#         val_r2_avg = val_r2_avg + val_r2\n",
    "#         val_nrmse_avg = val_nrmse_avg + val_nrmse\n",
    "#\n",
    "#         if plot:\n",
    "#             make_plot(y_train, y_pred_train, title=\"Train\", feature_names=args.targets, client=cid)\n",
    "#             make_plot(y_val, y_pred_val, title=\"Val\", feature_names=args.targets, client=cid)\n",
    "#\n",
    "#     print(\"总长度：\" + str(len(cids)))\n",
    "#     val_mse_avg = val_mse_avg / len(cids)\n",
    "#     val_rmse_avg = val_rmse_avg / len(cids)\n",
    "#     val_mae_avg = val_mae_avg / len(cids)\n",
    "#     val_r2_avg = val_r2_avg / len(cids)\n",
    "#     val_nrmse_avg = val_nrmse_avg / len(cids)\n",
    "#     print(f\"[Val]: val_mse_avg: {val_mse_avg}, \"\n",
    "#           f\"val_rmse_avg: {val_rmse_avg}, val_mae_avg: {val_mae_avg}, val_r2_avg: {val_r2_avg}, val_nrmse_avg: {val_nrmse_avg}\\n\\n\")\n",
    "#\n",
    "#\n",
    "# inference(\n",
    "#     global_model,\n",
    "#     client_X_train,\n",
    "#     client_y_train,\n",
    "#     client_X_val,\n",
    "#     client_y_val,\n",
    "#     exogenous_data_train,\n",
    "#     exogenous_data_val,\n",
    "#     y_scalers\n",
    "# )"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "start_time": "2023-08-13T23:36:50.928678Z",
     "end_time": "2023-08-13T23:36:50.972687Z"
    }
   }
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3.9.10 64-bit",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.10"
  },
  "vscode": {
   "interpreter": {
    "hash": "a39106e1a9d6d153b7400628e7589ff266b5caee5b0db427f0903be982155882"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
